mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 18:32:03 +00:00
206 lines
4.9 KiB
Plaintext
206 lines
4.9 KiB
Plaintext
/*
|
|
File: Parser
|
|
*/
|
|
/*
|
|
Class: n_Parser
|
|
An object that reads tokens and produces an AST (abstract syntax tree).
|
|
*/
|
|
/datum/n_Parser
|
|
/*
|
|
Var: index
|
|
The parser's current position in the token's list.
|
|
*/
|
|
var/index = 1
|
|
/*
|
|
Var: tokens
|
|
A list of tokens in the source code generated by a scanner.
|
|
*/
|
|
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.
|
|
|
|
See Also:
|
|
- <scriptError>
|
|
*/
|
|
var/list/errors = new
|
|
/*
|
|
Var: warnings
|
|
A list of non-fatal problems in the script.
|
|
*/
|
|
var/list/warnings = new
|
|
/*
|
|
Var: curToken
|
|
The token at <index> in <tokens>.
|
|
*/
|
|
var/datum/token/curToken
|
|
var/datum/stack/blocks = new
|
|
var/datum/node/BlockDefinition/GlobalBlock/global_block = new
|
|
var/datum/node/BlockDefinition/GlobalBlock/curBlock
|
|
|
|
/*
|
|
Proc: Parse
|
|
Reads the tokens and returns the AST's <GlobalBlock> node. Be sure to populate the tokens list before calling this procedure.
|
|
*/
|
|
/datum/n_Parser/proc/Parse()
|
|
return
|
|
/*
|
|
Proc: NextToken
|
|
Sets <curToken> to the next token in the <tokens> list, or null if there are no more tokens.
|
|
*/
|
|
/datum/n_Parser/proc/NextToken()
|
|
if(index >= tokens.len)
|
|
curToken = null
|
|
else
|
|
curToken = tokens[++index]
|
|
return curToken
|
|
|
|
/*
|
|
Class: nS_Parser
|
|
An implmentation of a parser for n_Script.
|
|
*/
|
|
/datum/n_Parser/nS_Parser
|
|
var/datum/n_scriptOptions/nS_Options/options
|
|
/*
|
|
Constructor: New
|
|
|
|
Parameters:
|
|
tokens - A list of tokens to parse.
|
|
options - An object used for configuration.
|
|
*/
|
|
/datum/n_Parser/nS_Parser/New(tokens[], var/datum/n_scriptOptions/options)
|
|
src.tokens = tokens
|
|
src.options = options
|
|
curBlock = global_block
|
|
return ..()
|
|
|
|
/datum/n_Parser/nS_Parser/Parse()
|
|
ASSERT(tokens)
|
|
for(,src.index <= src.tokens.len, src.index++)
|
|
curToken = tokens[index]
|
|
switch(curToken.type)
|
|
if(/datum/token/keyword)
|
|
var/datum/n_Keyword/kw = options.keywords[curToken.value]
|
|
kw = new kw()
|
|
if(kw)
|
|
if(!kw.Parse(src))
|
|
return
|
|
|
|
if(/datum/token/word)
|
|
var/datum/token/ntok
|
|
if(index + 1 > tokens.len)
|
|
errors += new/datum/scriptError/BadToken(curToken)
|
|
continue
|
|
|
|
ntok = tokens[index + 1]
|
|
|
|
if(!istype(ntok, /datum/token/symbol))
|
|
errors += new/datum/scriptError/BadToken(ntok)
|
|
continue
|
|
|
|
if(ntok.value == "(")
|
|
ParseFunctionStatement()
|
|
|
|
else if(options.assign_operators.Find(ntok.value))
|
|
ParseAssignment()
|
|
|
|
else
|
|
errors += new/datum/scriptError/BadToken(ntok)
|
|
continue
|
|
|
|
if(!istype(curToken, /datum/token/end))
|
|
errors += new/datum/scriptError/ExpectedToken(";", curToken)
|
|
continue
|
|
|
|
if(/datum/token/symbol)
|
|
if(curToken.value == "}")
|
|
if(!EndBlock())
|
|
errors += new/datum/scriptError/BadToken(curToken)
|
|
continue
|
|
|
|
else
|
|
errors += new/datum/scriptError/BadToken(curToken)
|
|
continue
|
|
|
|
if(/datum/token/end)
|
|
warnings += new/datum/scriptError/BadToken(curToken)
|
|
continue
|
|
|
|
else
|
|
errors += new/datum/scriptError/BadToken(curToken)
|
|
return
|
|
|
|
return global_block
|
|
|
|
/datum/n_Parser/nS_Parser/proc/CheckToken(val, type, err = 1, skip = 1)
|
|
if(curToken.value != val || !istype(curToken, type))
|
|
if(err)
|
|
errors += new/datum/scriptError/ExpectedToken(val, curToken)
|
|
return 0
|
|
if(skip)
|
|
NextToken()
|
|
|
|
return 1
|
|
|
|
/datum/n_Parser/nS_Parser/proc/AddBlock(var/datum/node/BlockDefinition/B)
|
|
blocks.Push(curBlock)
|
|
curBlock = B
|
|
|
|
/datum/n_Parser/nS_Parser/proc/EndBlock()
|
|
if(curBlock == global_block)
|
|
return 0
|
|
|
|
curBlock = blocks.Pop()
|
|
return 1
|
|
|
|
/datum/n_Parser/nS_Parser/proc/ParseAssignment()
|
|
var/name = curToken.value
|
|
if(!options.IsValidID(name))
|
|
errors += new/datum/scriptError/InvalidID(curToken)
|
|
return
|
|
|
|
NextToken()
|
|
var/t = options.binary_operators[options.assign_operators[curToken.value]]
|
|
var/datum/node/statement/VariableAssignment/stmt = new()
|
|
stmt.var_name = new(name)
|
|
NextToken()
|
|
|
|
if(t)
|
|
stmt.value = new t()
|
|
stmt.value:exp = new/datum/node/expression/value/variable(stmt.var_name)
|
|
stmt.value:exp2 = ParseExpression()
|
|
else
|
|
stmt.value = ParseExpression()
|
|
|
|
curBlock.statements += stmt
|
|
|
|
/datum/n_Parser/nS_Parser/proc/ParseFunctionStatement()
|
|
if(!istype(curToken, /datum/token/word))
|
|
errors += new/datum/scriptError("Bad identifier in function call.")
|
|
return
|
|
|
|
var/datum/node/statement/FunctionCall/stmt=new
|
|
stmt.func_name = curToken.value
|
|
NextToken() //skip function name
|
|
if(!CheckToken("(", /datum/token/symbol)) //Check for and skip open parenthesis
|
|
return
|
|
var/loops = 0
|
|
while(TRUE)
|
|
loops++
|
|
if(loops >= 800)
|
|
errors +=new/datum/scriptError("Cannot find ending params.")
|
|
return
|
|
|
|
if(!curToken)
|
|
errors+=new/datum/scriptError/EndOfFile()
|
|
return
|
|
if(istype(curToken, /datum/token/symbol) && curToken.value == ")")
|
|
curBlock.statements += stmt
|
|
NextToken() //Skip close parenthesis
|
|
return
|
|
|
|
var/datum/node/expression/P = ParseParamExpression(check_functions = 1)
|
|
stmt.parameters += P
|
|
if(istype(curToken, /datum/token/symbol) && curToken.value == ",")
|
|
NextToken()
|