// Change the scope state // Report an error if the token is being used in the wrong place (such as a declaration // after an executable statement). bool ChangeState(SimpleToken token) { BlockState newState = TokenToState(token); if (newState != BlockState.SUBFUNC && newState < _state) { _messages.Error(MessageCode.TOKENNOTPERMITTED, String.Format("{0} not permitted here", token)); return false; } if (newState != BlockState.UNORDERED) { _state = newState; } if (newState == BlockState.STATEMENT) { if (_currentProcedure == null) { _ls.BackToken(); _ptree.Add(KCreateProgram(_entryPointName)); return false; } } return true; }
/// Returns the block state to which the specified token belongs. For example, /// IMPLICIT must precede any declaration which must precede any executable /// statement in the same program group. BlockState TokenToState(SimpleToken token) { BlockState state = BlockState.NONE; switch (token.ID) { case TokenID.KPROGRAM: state = BlockState.PROGRAM; break; case TokenID.KIMPLICITNONE: state = BlockState.IMPLICITNONE; break; case TokenID.KIMPLICIT: state = BlockState.IMPLICIT; break; case TokenID.KFORMAT: case TokenID.KINCLUDE: // May appear anywhere. state = BlockState.UNORDERED; break; case TokenID.KINTEGER: case TokenID.KREAL: case TokenID.KCHARACTER: case TokenID.KDIMENSION: case TokenID.KLOGICAL: case TokenID.KDPRECISION: case TokenID.KCOMPLEX: case TokenID.KCOMMON: case TokenID.KPARAMETER: case TokenID.KINTRINSIC: case TokenID.KEXTERNAL: case TokenID.KEQUIVALENCE: case TokenID.KSAVE: state = BlockState.SPECIFICATION; break; case TokenID.IDENT: case TokenID.KSTMTFUNC: case TokenID.KCALL: case TokenID.KCLOSE: case TokenID.KCONTINUE: case TokenID.KDO: case TokenID.KEND: case TokenID.KGO: case TokenID.KGOTO: case TokenID.KIF: case TokenID.KINQUIRE: case TokenID.KOPEN: case TokenID.KPAUSE: case TokenID.KPRINT: case TokenID.KREAD: case TokenID.KRETURN: case TokenID.KSTOP: case TokenID.KWRITE: case TokenID.KENDFILE: case TokenID.KREWIND: case TokenID.KBACKSPACE: case TokenID.KASSIGN: case TokenID.KDATA: case TokenID.KENTRY: state = BlockState.STATEMENT; break; case TokenID.KSUBROUTINE: case TokenID.KFUNCTION: case TokenID.KBLOCKDATA: state = BlockState.SUBFUNC; break; } return state; }
/// <summary> /// Get the next keyword from the source file, or TokenID.IDENT if the keyword /// isn't recognised but looks like a valid identifier. /// /// This function also handles merging keywords that are represented by /// multiple tokens into a single keyword token. Thus: /// /// GO TO -> GOTO /// DOUBLE PRECISION -> DOUBLEPRECISION /// (etc...) /// /// </summary> /// <returns>A SimpleToken representing the keyword found</returns> public SimpleToken GetKeyword() { SimpleToken token = GetToken(); if (!(token is IdentifierToken)) { return token; } TokenID id = token.KeywordID; if (id == TokenID.IDENT) { return token; } if (id == TokenID.KGO) { // BUGBUG: Hack until full tokeniser implemented. LocalExpectToken(TokenID.KTO); id = TokenID.KGOTO; _tokens[_tindex - 2] = new SimpleToken(id); _tokens.RemoveAt(--_tindex); } else if (id == TokenID.KEND && PeekKeyword() == TokenID.KIF) { // BUGBUG: Hack until full tokeniser implemented. LocalExpectToken(TokenID.KIF); id = TokenID.KENDIF; _tokens[_tindex - 2] = new SimpleToken(id); _tokens.RemoveAt(--_tindex); } else if (id == TokenID.KELSE && PeekKeyword() == TokenID.KIF) { // BUGBUG: Hack until full tokeniser implemented. LocalExpectToken(TokenID.KIF); id = TokenID.KELSEIF; _tokens[_tindex - 2] = new SimpleToken(id); _tokens.RemoveAt(--_tindex); } else if (id == TokenID.KDOUBLE) { // BUGBUG: Hack until full tokeniser implemented. LocalExpectToken(TokenID.KPRECISION); id = TokenID.KDPRECISION; _tokens[_tindex - 2] = new SimpleToken(id); _tokens.RemoveAt(--_tindex); } else if (id == TokenID.KBLOCK) { // BUGBUG: Hack until full tokeniser implemented. LocalExpectToken(TokenID.KDATA); id = TokenID.KBLOCKDATA; _tokens[_tindex - 2] = new SimpleToken(id); _tokens.RemoveAt(--_tindex); } else if (id == TokenID.KIMPLICIT && PeekKeyword() == TokenID.KNONE) { GetToken(); id = TokenID.KIMPLICITNONE; } return new SimpleToken(id); }
// Parse one statement. // Note that this may be called as part of a logical or block IF statement. Non-executable statements // won't be allowed in this context but, happily, if we're parsing an IF statement then we're already // in STATEMENT state. ParseNode Statement(SimpleToken token) { // First make sure this statement is allowed here. if (ChangeState(token)) { switch (token.ID) { case TokenID.KASSIGN: return KAssign(); case TokenID.KBACKSPACE: return KBackspace(); case TokenID.KBLOCKDATA: return KBlockData(); case TokenID.KCALL: return KCall(); case TokenID.KCHARACTER: return KCharacter(); case TokenID.KCLOSE: return KClose(); case TokenID.KCOMMON: return KCommon(); case TokenID.KCOMPLEX: return KComplex(); case TokenID.KCONTINUE: return KContinue(); case TokenID.KDATA: return KData(); case TokenID.KDIMENSION: return KDimension(); case TokenID.KDO: return KDo(); case TokenID.KDPRECISION: return KDouble(); case TokenID.KENDFILE: return KEndFile(); case TokenID.KENTRY: return KEntry(); case TokenID.KEQUIVALENCE: return KEquivalence(); case TokenID.KEXTERNAL: return KExternal(); case TokenID.KFORMAT: return KFormat(); case TokenID.KFUNCTION: return KFunction(); case TokenID.KGOTO: return KGoto(); case TokenID.KIF: return KIf(); case TokenID.KIMPLICIT: return KImplicit(); case TokenID.KIMPLICITNONE: return KImplicitNone(); case TokenID.KINCLUDE: return KInclude(); case TokenID.KINQUIRE: return KInquire(); case TokenID.KINTEGER: return KInteger(); case TokenID.KINTRINSIC: return KIntrinsic(); case TokenID.KLOGICAL: return KLogical(); case TokenID.KOPEN: return KOpen(); case TokenID.KPAUSE: return KPause(); case TokenID.KPARAMETER: return KParameter(); case TokenID.KPRINT: return KPrint(); case TokenID.KPROGRAM: return KProgram(); case TokenID.KREAD: return KRead(); case TokenID.KREAL: return KReal(); case TokenID.KRETURN: return KReturn(); case TokenID.KREWIND: return KRewind(); case TokenID.KSAVE: return KSave(); case TokenID.KSTOP: return KStop(); case TokenID.KSUBROUTINE: return KSubroutine(); case TokenID.KWRITE: return KWrite(); case TokenID.IDENT: return KAssignment(); } // Anything else is unparseable, so assume the rest of the line is // too and skip it. _messages.Error(MessageCode.UNEXPECTEDTOKEN, _ls.LineNumber, String.Format("Unexpected {0} found in statement", token)); SkipToEndOfLine(); } return null; }
// Parse an identifier parse node from the specified token. IdentifierParseNode ParseIdentifierParseNode() { IdentifierParseNode node = new IdentifierParseNode(null); Collection<ParseNode> indices = null; SimpleToken token = _ls.GetToken(); bool isSubstring = false; while (token.ID == TokenID.LPAREN) { if (indices == null) { indices = new Collection<ParseNode>(); } if (_ls.PeekToken().ID != TokenID.RPAREN) { do { ParseNode item = null; if (_ls.PeekToken().ID == TokenID.RPAREN) { SkipToken(TokenID.RPAREN); break; } if (_ls.PeekToken().ID != TokenID.COLON) { item = Expression(); } token = _ls.GetToken(); if (token.ID == TokenID.COLON) { isSubstring = true; if (item == null) { item = new NumberParseNode(1); } node.SubstringStart = item; token = new SimpleToken(TokenID.COMMA); continue; } if (isSubstring) { node.SubstringEnd = item; break; } indices.Add(item); } while (token.ID == TokenID.COMMA); _ls.BackToken(); } ExpectToken(TokenID.RPAREN); token = _ls.GetToken(); } node.Indexes = indices; _ls.BackToken(); return node; }