/* 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: - */ var/list/errors = new /* Var: warnings A list of non-fatal problems in the script. */ var/list/warnings = new /* Var: curToken The token at in . */ 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 node. Be sure to populate the tokens list before calling this procedure. */ /datum/n_Parser/proc/Parse() return /* Proc: NextToken Sets to the next token in the 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 for() 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()