/******************************************************************************* * * Copyright (c) 2008 Fujitsu Services Ltd. * * Author: Nick Battle * * This file is part of VDMJ. * * VDMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VDMJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.parser.syntax; import java.util.Arrays; import java.util.List; import java.util.Vector; import org.overture.ast.definitions.AAssignmentDefinition; import org.overture.ast.definitions.AEqualsDefinition; import org.overture.ast.definitions.AImplicitOperationDefinition; import org.overture.ast.definitions.AInstanceVariableDefinition; import org.overture.ast.definitions.APrivateAccess; import org.overture.ast.definitions.AProtectedAccess; import org.overture.ast.definitions.APublicAccess; import org.overture.ast.definitions.ATypeDefinition; import org.overture.ast.definitions.AValueDefinition; import org.overture.ast.definitions.PAccess; import org.overture.ast.definitions.PDefinition; import org.overture.ast.definitions.traces.ATraceDefinitionTerm; import org.overture.ast.definitions.traces.PTraceCoreDefinition; import org.overture.ast.definitions.traces.PTraceDefinition; import org.overture.ast.expressions.AEqualsBinaryExp; import org.overture.ast.expressions.PExp; import org.overture.ast.factory.AstFactory; import org.overture.ast.intf.lex.ILexLocation; import org.overture.ast.intf.lex.ILexNameToken; import org.overture.ast.intf.lex.ILexToken; import org.overture.ast.lex.Dialect; import org.overture.ast.lex.LexIdentifierToken; import org.overture.ast.lex.LexIntegerToken; import org.overture.ast.lex.LexLocation; import org.overture.ast.lex.LexNameList; import org.overture.ast.lex.LexNameToken; import org.overture.ast.lex.LexToken; import org.overture.ast.lex.VDMToken; import org.overture.ast.node.tokens.TStatic; import org.overture.ast.patterns.APatternListTypePair; import org.overture.ast.patterns.APatternTypePair; import org.overture.ast.patterns.ASeqBind; import org.overture.ast.patterns.ASetBind; import org.overture.ast.patterns.ATypeBind; import org.overture.ast.patterns.PMultipleBind; import org.overture.ast.patterns.PPattern; import org.overture.ast.statements.ACallObjectStm; import org.overture.ast.statements.ACallStm; import org.overture.ast.statements.AErrorCase; import org.overture.ast.statements.AExternalClause; import org.overture.ast.statements.ASpecificationStm; import org.overture.ast.statements.PStm; import org.overture.ast.typechecker.NameScope; import org.overture.ast.types.AAccessSpecifierAccessSpecifier; import org.overture.ast.types.AFieldField; import org.overture.ast.types.AFunctionType; import org.overture.ast.types.ANamedInvariantType; import org.overture.ast.types.AOperationType; import org.overture.ast.types.AUnresolvedType; import org.overture.ast.types.PType; import org.overture.ast.types.SInvariantType; import org.overture.config.Release; import org.overture.config.Settings; import org.overture.parser.config.Properties; import org.overture.parser.lex.LexException; import org.overture.parser.lex.LexTokenReader; import org.overture.parser.messages.LocatedException; /** * A syntax analyser to parse definitions. */ public class DefinitionReader extends SyntaxReader { public DefinitionReader(LexTokenReader reader) { super(reader); } private static VDMToken[] sectionArray = { VDMToken.TYPES, VDMToken.FUNCTIONS, VDMToken.STATE, VDMToken.VALUES, VDMToken.OPERATIONS, VDMToken.INSTANCE, VDMToken.THREAD, VDMToken.SYNC, VDMToken.TRACES, VDMToken.END, VDMToken.EOF }; private static VDMToken[] afterArray = { VDMToken.SEMICOLON }; private static List<VDMToken> sectionList = Arrays.asList(sectionArray); private boolean newSection() throws LexException { return newSection(lastToken()); } public static boolean newSection(LexToken tok) { return sectionList.contains(tok.type); } public List<PDefinition> readDefinitions() throws ParserException, LexException { List<PDefinition> list = new Vector<PDefinition>(); boolean threadDone = false; while (lastToken().isNot(VDMToken.EOF) && lastToken().isNot(VDMToken.END)) { switch (lastToken().type) { case TYPES: list.addAll(readTypes()); break; case FUNCTIONS: list.addAll(readFunctions()); break; case STATE: if (dialect != Dialect.VDM_SL) { throwMessage(2277, "Can't have state in VDM++"); } try { nextToken(); list.add(readStateDefinition()); if (!newSection()) { checkFor(VDMToken.SEMICOLON, 2080, "Missing ';' after state definition"); } } catch (LocatedException e) { report(e, afterArray, sectionArray); } break; case VALUES: list.addAll(readValues()); break; case OPERATIONS: list.addAll(readOperations()); break; case INSTANCE: if (dialect == Dialect.VDM_SL) { throwMessage(2009, "Can't have instance variables in VDM-SL"); } list.addAll(readInstanceVariables()); break; case TRACES: if (dialect == Dialect.VDM_SL && Settings.release != Release.VDM_10) { throwMessage(2262, "Can't have traces in VDM-SL classic"); } list.addAll(readTraces()); break; case THREAD: if (dialect == Dialect.VDM_SL) { throwMessage(2010, "Can't have a thread clause in VDM-SL"); } if (!threadDone) { threadDone = true; } else { throwMessage(2011, "Only one thread clause permitted per class"); } try { nextToken(); list.add(readThreadDefinition()); if (!newSection()) { checkFor(VDMToken.SEMICOLON, 2085, "Missing ';' after thread definition"); } } catch (LocatedException e) { report(e, afterArray, sectionArray); } break; case SYNC: if (dialect == Dialect.VDM_SL) { throwMessage(2012, "Can't have a sync clause in VDM-SL"); } list.addAll(readSyncs()); break; case EOF: break; default: try { throwMessage(2013, "Expected 'operations', 'state', 'functions', 'types' or 'values'"); } catch (LocatedException e) { report(e, afterArray, sectionArray); } } } return list; } private AAccessSpecifierAccessSpecifier readAccessSpecifier(boolean asyncOK, boolean pureOK) throws LexException, ParserException { if (dialect == Dialect.VDM_SL) { if (lastToken().is(VDMToken.PURE)) { if (Settings.release == Release.CLASSIC) { throwMessage(2325, "Pure operations are not available in classic"); } if (pureOK) { nextToken(); return AstFactory.newAAccessSpecifierAccessSpecifier(new APrivateAccess(), false, false, true); } else { throwMessage(2324, "Pure only permitted for operations"); } } else { return AstFactory.getDefaultAccessSpecifier(); } } // Defaults // boolean isStatic = false; // boolean isAsync = false; boolean isStatic = false; boolean isAsync = false; boolean isPure = false; // VDMToken access = VDMToken.PRIVATE; PAccess access = new APrivateAccess(); // Keyword counts int numStatic = 0; int numAsync = 0; int numPure = 0; int numAccess = 0; boolean more = true; while (more) { switch (lastToken().type) { case ASYNC: if (asyncOK) { if (++numAsync > 1) { throwMessage(2329, "Duplicate async keyword"); } isAsync = true; nextToken(); } else { throwMessage(2278, "Async only permitted for operations"); more = false; } break; case STATIC: if (++numStatic > 1) { throwMessage(2329, "Duplicate static keyword"); } isStatic = true; nextToken(); break; case PUBLIC: if (++numAccess > 1) { throwMessage(2329, "Duplicate access specifier keyword"); } access = new APublicAccess(); nextToken(); break; case PRIVATE: if (++numAccess > 1) { throwMessage(2329, "Duplicate access specifier keyword"); } access = new APrivateAccess(); nextToken(); break; case PROTECTED: if (++numAccess > 1) { throwMessage(2329, "Duplicate access specifier keyword"); } access = new AProtectedAccess(); nextToken(); break; case PURE: if (Settings.release == Release.CLASSIC) { throwMessage(2325, "Pure operations are not available in classic"); } if (pureOK) { if (++numPure > 1) { throwMessage(2329, "Duplicate pure keyword"); } isPure = true; nextToken(); } else { throwMessage(2324, "Pure only permitted for operations"); } break; default: more = false; break; } } return AstFactory.newAAccessSpecifierAccessSpecifier(access, isStatic, isAsync, isPure); } public ATypeDefinition readTypeDefinition() throws ParserException, LexException { LexIdentifierToken id = readIdToken("Expecting new type identifier"); TypeReader tr = getTypeReader(); SInvariantType invtype = null; switch (lastToken().type) { case EQUALS: nextToken(); PType type = tr.readType(); ANamedInvariantType nt = AstFactory.newANamedInvariantType(idToName(id), type); if (type instanceof AUnresolvedType && ((AUnresolvedType) type).getName().equals(idToName(id))) { throwMessage(2014, "Recursive type declaration"); } invtype = nt; break; case COLONCOLON: nextToken(); invtype = AstFactory.newARecordInvariantType(idToName(id), tr.readFieldList()); break; default: throwMessage(2015, "Expecting =<type> or ::<field list>"); } PPattern invPattern = null; PExp invExpression = null; if (lastToken().is(VDMToken.INV)) { nextToken(); invPattern = getPatternReader().readPattern(); checkFor(VDMToken.EQUALSEQUALS, 2087, "Expecting '==' after pattern in invariant"); invExpression = getExpressionReader().readExpression(); } return AstFactory.newATypeDefinition(idToName(id), invtype, invPattern, invExpression); } private List<PDefinition> readTypes() throws LexException, ParserException { checkFor(VDMToken.TYPES, 2013, "Expected 'types'"); List<PDefinition> list = new Vector<PDefinition>(); while (!newSection()) { try { AAccessSpecifierAccessSpecifier access = readAccessSpecifier(false, false); access.setStatic(new TStatic()); ATypeDefinition def = readTypeDefinition(); // Force all type defs (invs) to be static def.setAccess(access); list.add(def); if (!newSection()) { checkFor(VDMToken.SEMICOLON, 2078, "Missing ';' after type definition"); } } catch (LocatedException e) { report(e, afterArray, sectionArray); } } return list; } private List<PDefinition> readValues() throws LexException, ParserException { checkFor(VDMToken.VALUES, 2013, "Expected 'values'"); List<PDefinition> list = new Vector<PDefinition>(); while (!newSection()) { try { AAccessSpecifierAccessSpecifier access = readAccessSpecifier(false, false); access.setStatic(new TStatic()); PDefinition def = readValueDefinition(NameScope.GLOBAL); // Force all values to be static def.setAccess(access); if (def instanceof AValueDefinition) { for (PDefinition pDefinition : ((AValueDefinition) def).getDefs()) { pDefinition.setAccess(access.clone()); } } list.add(def); if (!newSection()) { checkFor(VDMToken.SEMICOLON, 2081, "Missing ';' after value definition"); } } catch (LocatedException e) { report(e, afterArray, sectionArray); } } return list; } private List<PDefinition> readFunctions() throws LexException, ParserException { checkFor(VDMToken.FUNCTIONS, 2013, "Expected 'functions'"); List<PDefinition> list = new Vector<PDefinition>(); while (!newSection()) { try { AAccessSpecifierAccessSpecifier access = readAccessSpecifier(false, false); PDefinition def = readFunctionDefinition(NameScope.GLOBAL); if (Settings.release == Release.VDM_10) { // Force all functions to be static for VDM-10 access.setStatic(new TStatic()); def.setAccess(access); } else { def.setAccess(access); } list.add(def); if (!newSection()) { checkFor(VDMToken.SEMICOLON, 2079, "Missing ';' after function definition"); } } catch (LocatedException e) { report(e, afterArray, sectionArray); } } return list; } public List<PDefinition> readOperations() throws LexException, ParserException { checkFor(VDMToken.OPERATIONS, 2013, "Expected 'operations'"); List<PDefinition> list = new Vector<PDefinition>(); while (!newSection()) { try { AAccessSpecifierAccessSpecifier access = readAccessSpecifier(dialect == Dialect.VDM_RT, true); PDefinition def = readOperationDefinition(); def.setAccess(access); ((AOperationType)def.getType()).setPure(access.getPure()); list.add(def); if (!newSection()) { checkFor(VDMToken.SEMICOLON, 2082, "Missing ';' after operation definition"); } } catch (LocatedException e) { report(e, afterArray, sectionArray); } } return list; } public List<PDefinition> readInstanceVariables() throws LexException, ParserException { checkFor(VDMToken.INSTANCE, 2083, "Expected 'instance variables'"); checkFor(VDMToken.VARIABLES, 2083, "Expecting 'instance variables'"); List<PDefinition> list = new Vector<PDefinition>(); while (!newSection()) { try { PDefinition def = readInstanceVariableDefinition(); list.add(def); if (!newSection()) { checkFor(VDMToken.SEMICOLON, 2084, "Missing ';' after instance variable definition"); } } catch (LocatedException e) { report(e, afterArray, sectionArray); } } return list; } private List<PDefinition> readTraces() throws LexException, ParserException { checkFor(VDMToken.TRACES, 2013, "Expected 'traces'"); List<PDefinition> list = new Vector<PDefinition>(); while (!newSection()) { try { PDefinition def = readNamedTraceDefinition(); list.add(def); if (!newSection()) { ignore(VDMToken.SEMICOLON); // Optional? } } catch (LocatedException e) { report(e, afterArray, sectionArray); } } return list; } private List<PDefinition> readSyncs() throws LexException, ParserException { checkFor(VDMToken.SYNC, 2013, "Expected 'sync'"); List<PDefinition> list = new Vector<PDefinition>(); while (!newSection()) { try { PDefinition def = readPermissionPredicateDefinition(); list.add(def); if (!newSection()) { checkFor(VDMToken.SEMICOLON, 2086, "Missing ';' after sync definition"); } } catch (LocatedException e) { report(e, afterArray, sectionArray); } } return list; } public LexNameList readTypeParams() throws LexException, ParserException { LexNameList typeParams = null; if (lastToken().is(VDMToken.SEQ_OPEN)) { typeParams = new LexNameList(); nextToken(); checkFor(VDMToken.AT, 2088, "Expecting '@' before type parameter"); LexIdentifierToken tid = readIdToken("Expecting '@identifier' in type parameter list"); typeParams.add(idToName(tid)); while (ignore(VDMToken.COMMA)) { checkFor(VDMToken.AT, 2089, "Expecting '@' before type parameter"); tid = readIdToken("Expecting '@identifier' in type parameter list"); typeParams.add(idToName(tid)); } checkFor(VDMToken.SEQ_CLOSE, 2090, "Expecting ']' after type parameters"); } return typeParams; } private PDefinition readFunctionDefinition(NameScope scope) throws ParserException, LexException { PDefinition def = null; LexIdentifierToken funcName = readIdToken("Expecting new function identifier"); if (funcName.getName().startsWith("mk_")) { throwMessage(2016, "Function name cannot start with 'mk_'"); } LexNameList typeParams = readTypeParams(); if (lastToken().is(VDMToken.COLON)) { def = readExplicitFunctionDefinition(funcName, scope, typeParams); } else if (lastToken().is(VDMToken.BRA)) { def = readImplicitFunctionDefinition(funcName, scope, typeParams); } else { throwMessage(2017, "Expecting ':' or '(' after name in function definition"); } LexLocation.addSpan(idToName(funcName), lastToken()); return def; } private PDefinition readExplicitFunctionDefinition( LexIdentifierToken funcName, NameScope scope, List<ILexNameToken> typeParams) throws ParserException, LexException { // Explicit function definition, like "f: int->bool f(x) == true" nextToken(); PType t = getTypeReader().readType(); if (!(t instanceof AFunctionType)) { throwMessage(2018, "Function type is not a -> or +> function"); } AFunctionType type = (AFunctionType) t; LexIdentifierToken name = readIdToken("Expecting identifier after function type in definition"); if (!name.equals(funcName)) { throwMessage(2019, "Expecting identifier " + funcName.getName() + " after type in definition"); } if (lastToken().isNot(VDMToken.BRA)) { throwMessage(2020, "Expecting '(' after function name"); } List<List<PPattern>> parameters = new Vector<List<PPattern>>(); while (lastToken().is(VDMToken.BRA)) { if (nextToken().isNot(VDMToken.KET)) { parameters.add(getPatternReader().readPatternList()); checkFor(VDMToken.KET, 2091, "Expecting ')' after function parameters"); } else { parameters.add(new Vector<PPattern>()); // empty "()" nextToken(); } } checkFor(VDMToken.EQUALSEQUALS, 2092, "Expecting '==' after parameters"); ExpressionReader expr = getExpressionReader(); PExp body = readFunctionBody(); PExp precondition = null; PExp postcondition = null; LexNameToken measure = null; if (lastToken().is(VDMToken.PRE)) { nextToken(); precondition = expr.readExpression(); } if (lastToken().is(VDMToken.POST)) { nextToken(); postcondition = expr.readExpression(); } if (lastToken().is(VDMToken.MEASURE)) { nextToken(); measure = readNameToken("Expecting name after 'measure'"); } return AstFactory.newAExplicitFunctionDefinition(idToName(funcName), scope, typeParams, type, parameters, body, precondition, postcondition, false, measure); } private PDefinition readImplicitFunctionDefinition( LexIdentifierToken funcName, NameScope scope, List<ILexNameToken> typeParams) throws ParserException, LexException { // Implicit, like g(x: int) y: bool pre exp post exp nextToken(); PatternReader pr = getPatternReader(); TypeReader tr = getTypeReader(); List<APatternListTypePair> parameterPatterns = new Vector<APatternListTypePair>(); if (lastToken().isNot(VDMToken.KET)) { List<PPattern> pl = pr.readPatternList(); checkFor(VDMToken.COLON, 2093, "Missing colon after pattern/type parameter"); parameterPatterns.add(AstFactory.newAPatternListTypePair(pl, tr.readType())); while (ignore(VDMToken.COMMA)) { pl = pr.readPatternList(); checkFor(VDMToken.COLON, 2093, "Missing colon after pattern/type parameter"); parameterPatterns.add(AstFactory.newAPatternListTypePair(pl, tr.readType())); } } checkFor(VDMToken.KET, 2124, "Expecting ')' after parameters"); LexToken firstResult = lastToken(); List<PPattern> resultNames = new Vector<PPattern>(); List<PType> resultTypes = new Vector<PType>(); do { LexIdentifierToken rname = readIdToken("Expecting result identifier"); resultNames.add(AstFactory.newAIdentifierPattern(idToName(rname))); checkFor(VDMToken.COLON, 2094, "Missing colon in identifier/type return value"); resultTypes.add(tr.readType()); } while (ignore(VDMToken.COMMA)); if (lastToken().is(VDMToken.IDENTIFIER)) { throwMessage(2261, "Missing comma between return types?"); } APatternTypePair resultPattern = null; if (resultNames.size() > 1) { resultPattern = AstFactory.newAPatternTypePair(AstFactory.newATuplePattern(firstResult.location, resultNames), AstFactory.newAProductType(firstResult.location, resultTypes)); } else { resultPattern = AstFactory.newAPatternTypePair(resultNames.get(0), resultTypes.get(0)); } ExpressionReader expr = getExpressionReader(); PExp body = null; PExp precondition = null; PExp postcondition = null; LexNameToken measure = null; if (lastToken().is(VDMToken.EQUALSEQUALS)) // extended implicit function { nextToken(); body = readFunctionBody(); } if (lastToken().is(VDMToken.PRE)) { nextToken(); precondition = expr.readExpression(); } if (body == null) // Mandatory for standard implicit functions { checkFor(VDMToken.POST, 2095, "Implicit function must have post condition"); postcondition = expr.readExpression(); } else { if (lastToken().is(VDMToken.POST)) { nextToken(); postcondition = expr.readExpression(); } } if (lastToken().is(VDMToken.MEASURE)) { nextToken(); measure = readNameToken("Expecting name after 'measure'"); } return AstFactory.newAImplicitFunctionDefinition(idToName(funcName), scope, typeParams, parameterPatterns, resultPattern, body, precondition, postcondition, measure); } public PDefinition readLocalDefinition(NameScope scope) throws ParserException, LexException { ParserException funcDefError = null; try { reader.push(); PDefinition def = readFunctionDefinition(scope); reader.unpush(); return def; } catch (ParserException e) // Not a function then... { e.adjustDepth(reader.getTokensRead()); reader.pop(); funcDefError = e; } try { reader.push(); PDefinition def = readValueDefinition(scope); reader.unpush(); return def; } catch (ParserException e) { e.adjustDepth(reader.getTokensRead()); reader.pop(); throw e.deeperThan(funcDefError) ? e : funcDefError; } } public PDefinition readValueDefinition(NameScope scope) throws ParserException, LexException { // Should be <pattern>[:<type>]=<expression> PPattern p = getPatternReader().readPattern(); PType type = null; if (lastToken().is(VDMToken.COLON)) { nextToken(); type = getTypeReader().readType(); } checkFor(VDMToken.EQUALS, 2096, "Expecting <pattern>[:<type>]=<exp>"); return AstFactory.newAValueDefinition(p, scope, type, getExpressionReader().readExpression()); } private PDefinition readStateDefinition() throws ParserException, LexException { LexIdentifierToken name = readIdToken("Expecting identifier after 'state' definition"); checkFor(VDMToken.OF, 2097, "Expecting 'of' after state name"); List<AFieldField> fieldList = getTypeReader().readFieldList(); PExp invExpression = null; PExp initExpression = null; PPattern invPattern = null; PPattern initPattern = null; if (lastToken().is(VDMToken.INV)) { nextToken(); invPattern = getPatternReader().readPattern(); checkFor(VDMToken.EQUALSEQUALS, 2098, "Expecting '==' after pattern in invariant"); invExpression = getExpressionReader().readExpression(); } if (lastToken().is(VDMToken.INIT)) { nextToken(); initPattern = getPatternReader().readPattern(); checkFor(VDMToken.EQUALSEQUALS, 2099, "Expecting '==' after pattern in initializer"); initExpression = getExpressionReader().readExpression(); } // Be forgiving about the inv/init order if (lastToken().is(VDMToken.INV) && invExpression == null) { nextToken(); invPattern = getPatternReader().readPattern(); checkFor(VDMToken.EQUALSEQUALS, 2098, "Expecting '==' after pattern in invariant"); invExpression = getExpressionReader().readExpression(); } checkFor(VDMToken.END, 2100, "Expecting 'end' after state definition"); return AstFactory.newAStateDefinition(idToName(name), fieldList, invPattern, invExpression, initPattern, initExpression); } private PDefinition readOperationDefinition() throws ParserException, LexException { PDefinition def = null; LexIdentifierToken funcName = readIdToken("Expecting new operation identifier"); if (lastToken().is(VDMToken.COLON)) { def = readExplicitOperationDefinition(funcName); } else if (lastToken().is(VDMToken.BRA)) { def = readImplicitOperationDefinition(funcName); } else if (lastToken().is(VDMToken.SEQ_OPEN)) { throwMessage(2059, "Operations cannot have [@T] type parameters"); } else { throwMessage(2021, "Expecting ':' or '(' after name in operation definition"); } LexLocation.addSpan(idToName(funcName), lastToken()); return def; } private PDefinition readExplicitOperationDefinition( LexIdentifierToken funcName) throws ParserException, LexException { // Like "f: int ==> bool f(x) == <statement>" nextToken(); AOperationType type = getTypeReader().readOperationType(); LexIdentifierToken name = readIdToken("Expecting operation identifier after type in definition"); if (!name.equals(funcName)) { throwMessage(2022, "Expecting name " + funcName.getName() + " after type in definition"); } if (lastToken().isNot(VDMToken.BRA)) { throwMessage(2023, "Expecting '(' after operation name"); } List<PPattern> parameters = null; if (nextToken().isNot(VDMToken.KET)) { parameters = getPatternReader().readPatternList(); checkFor(VDMToken.KET, 2101, "Expecting ')' after operation parameters"); } else { parameters = new Vector<PPattern>(); // empty "()" nextToken(); } checkFor(VDMToken.EQUALSEQUALS, 2102, "Expecting '==' after parameters"); PStm body = readOperationBody(); PExp precondition = null; PExp postcondition = null; if (lastToken().is(VDMToken.PRE)) { nextToken(); precondition = getExpressionReader().readExpression(); } if (lastToken().is(VDMToken.POST)) { nextToken(); postcondition = getExpressionReader().readExpression(); } return AstFactory.newAExplicitOperationDefinition(idToName(funcName), type, parameters, precondition, postcondition, body); } private PDefinition readImplicitOperationDefinition( LexIdentifierToken funcName) throws ParserException, LexException { // Like g(x: int) [y: bool]? ext rd fred[:int] pre exp post exp nextToken(); PatternReader pr = getPatternReader(); TypeReader tr = getTypeReader(); List<APatternListTypePair> parameterPatterns = new Vector<APatternListTypePair>(); if (lastToken().isNot(VDMToken.KET)) { List<PPattern> pl = pr.readPatternList(); checkFor(VDMToken.COLON, 2103, "Missing colon after pattern/type parameter"); parameterPatterns.add(AstFactory.newAPatternListTypePair(pl, tr.readType())); while (ignore(VDMToken.COMMA)) { pl = pr.readPatternList(); checkFor(VDMToken.COLON, 2103, "Missing colon after pattern/type parameter"); parameterPatterns.add(AstFactory.newAPatternListTypePair(pl, tr.readType())); } } checkFor(VDMToken.KET, 2124, "Expecting ')' after args"); LexToken firstResult = lastToken(); APatternTypePair resultPattern = null; if (firstResult.is(VDMToken.IDENTIFIER)) { List<PPattern> resultNames = new Vector<PPattern>(); List<PType> resultTypes = new Vector<PType>(); do { LexIdentifierToken rname = readIdToken("Expecting result identifier"); resultNames.add(AstFactory.newAIdentifierPattern(idToName(rname))); checkFor(VDMToken.COLON, 2104, "Missing colon in identifier/type return value"); resultTypes.add(tr.readType()); } while (ignore(VDMToken.COMMA)); if (lastToken().is(VDMToken.IDENTIFIER)) { throwMessage(2261, "Missing comma between return types?"); } if (resultNames.size() > 1) { resultPattern = AstFactory.newAPatternTypePair(AstFactory.newATuplePattern(firstResult.location, resultNames), AstFactory.newAProductType(firstResult.location, resultTypes)); } else { resultPattern = AstFactory.newAPatternTypePair(resultNames.get(0), resultTypes.get(0)); } } PStm body = null; if (lastToken().is(VDMToken.EQUALSEQUALS)) // extended implicit operation { nextToken(); body = readOperationBody(); } ASpecificationStm spec = readSpecification(funcName.location, body == null); AImplicitOperationDefinition def = AstFactory.newAImplicitOperationDefinition(idToName(funcName), parameterPatterns, resultPattern, body, spec); return def; } public ASpecificationStm readSpecification(ILexLocation location, boolean postMandatory) throws ParserException, LexException { List<AExternalClause> externals = null; if (lastToken().is(VDMToken.EXTERNAL)) { externals = new Vector<AExternalClause>(); nextToken(); while (lastToken().is(VDMToken.READ) || lastToken().is(VDMToken.WRITE)) { externals.add(readExternal()); } if (externals.isEmpty()) { throwMessage(2024, "Expecting external declarations after 'ext'"); } } ExpressionReader expr = getExpressionReader(); PExp precondition = null; PExp postcondition = null; if (lastToken().is(VDMToken.PRE)) { nextToken(); precondition = expr.readExpression(); } if (postMandatory) // Mandatory for standard implicit operations { checkFor(VDMToken.POST, 2105, "Implicit operation must define a post condition"); postcondition = expr.readExpression(); } else { if (lastToken().is(VDMToken.POST)) { nextToken(); postcondition = expr.readExpression(); } } List<AErrorCase> errors = null; if (lastToken().is(VDMToken.ERRS)) { errors = new Vector<AErrorCase>(); nextToken(); while (lastToken() instanceof LexIdentifierToken) { LexIdentifierToken name = readIdToken("Expecting error identifier"); checkFor(VDMToken.COLON, 2106, "Expecting ':' after name in errs clause"); PExp left = expr.readExpression(); checkFor(VDMToken.ARROW, 2107, "Expecting '->' in errs clause"); PExp right = expr.readExpression(); errors.add(AstFactory.newAErrorCase(name, left, right)); } if (errors.isEmpty()) { throwMessage(2025, "Expecting <name>: exp->exp in errs clause"); } } return AstFactory.newASpecificationStm(location, externals, precondition, postcondition, errors); } private AExternalClause readExternal() throws ParserException, LexException { LexToken mode = lastToken(); if (mode.isNot(VDMToken.READ) && mode.isNot(VDMToken.WRITE)) { throwMessage(2026, "Expecting 'rd' or 'wr' after 'ext'"); } LexNameList names = new LexNameList(); nextToken(); names.add(readNameToken("Expecting name in external clause")); while (ignore(VDMToken.COMMA)) { names.add(readNameToken("Expecting name in external clause")); } PType type = null; if (lastToken().is(VDMToken.COLON)) { nextToken(); type = getTypeReader().readType(); } return AstFactory.newAExternalClause(mode, names, type); } public AEqualsDefinition readEqualsDefinition() throws ParserException, LexException { // The grammar here says the form of the definition should be // "def" <patternBind>=<expression> "in" <expression>, but since // a set bind is "s in set S" that naively parses as // "s in set (S = <expression>)". Talking to PGL, we have to // make a special parse here. It is one of these forms: // // "def" <pattern> "=" <expression> "in" ... // "def" <type bind> "=" <expression> "in" ... // "def" <pattern> "in set" <equals-expression> "in" ... // "def" <pattern> "in seq" <equals-expression> "in" ... // // and the "=" is unpicked from the left and right of the equals // expression in the last two cases. ILexLocation location = lastToken().location; ParserException equalsDefError = null; try // "def" <pattern> "=" <expression> "in" ... { reader.push(); PPattern pattern = getPatternReader().readPattern(); checkFor(VDMToken.EQUALS, 2108, "Expecting <pattern>=<exp>"); PExp test = getExpressionReader().readExpression(); reader.unpush(); return AstFactory.newAEqualsDefinition(location, pattern, test); } catch (ParserException e) { e.adjustDepth(reader.getTokensRead()); reader.pop(); equalsDefError = e; } try // "def" <type bind> "=" <expression> "in" ... { reader.push(); ATypeBind typebind = getBindReader().readTypeBind(); checkFor(VDMToken.EQUALS, 2109, "Expecting <type bind>=<exp>"); PExp test = getExpressionReader().readExpression(); reader.unpush(); return AstFactory.newAEqualsDefinition(location, typebind, test); } catch (ParserException e) { e.adjustDepth(reader.getTokensRead()); reader.pop(); equalsDefError = e.deeperThan(equalsDefError) ? e : equalsDefError; } try { reader.push(); PPattern pattern = getPatternReader().readPattern(); checkFor(VDMToken.IN, 2110, "Expecting <pattern> in set|seq <exp>"); AEqualsBinaryExp test = null; switch (lastToken().type) { case SET: // "def" <pattern> "in set" <equals-expression> "in" ... nextToken(); test = getExpressionReader().readDefEqualsExpression(); ASetBind setbind = AstFactory.newASetBind(pattern, test.getLeft()); reader.unpush(); return AstFactory.newAEqualsDefinition(location, setbind, test.getRight()); case SEQ: // "def" <pattern> "in seq" <equals-expression> "in" ... if (Settings.release == Release.CLASSIC) { throwMessage(2328, "Sequence binds are not available in classic"); } nextToken(); test = getExpressionReader().readDefEqualsExpression(); ASeqBind seqbind = AstFactory.newASeqBind(pattern, test.getLeft()); reader.unpush(); return AstFactory.newAEqualsDefinition(location, seqbind, test.getRight()); default: throwMessage(2111, "Expecting <pattern> in set|seq <exp>"); return null; } } catch (ParserException e) { e.adjustDepth(reader.getTokensRead()); reader.pop(); throw e.deeperThan(equalsDefError) ? e : equalsDefError; } } private PDefinition readInstanceVariableDefinition() throws ParserException, LexException { LexToken token = lastToken(); if (token.is(VDMToken.INV)) { nextToken(); PExp exp = getExpressionReader().readExpression(); String str = getCurrentModule(); LexNameToken className = new LexNameToken(str, str, token.location); return AstFactory.newAClassInvariantDefinition(className.getInvName(token.location), exp); } else { AAccessSpecifierAccessSpecifier access = readAccessSpecifier(false, false); AAssignmentDefinition def = getStatementReader().readAssignmentDefinition(); AInstanceVariableDefinition ivd = AstFactory.newAInstanceVariableDefinition(def.getName(), def.getType(), def.getExpression()); def.getType().parent(ivd);// the type of ivd is graph but we trough away the assignment ivd.setAccess(access); return ivd; } } private PDefinition readThreadDefinition() throws LexException, ParserException { LexToken token = lastToken(); if (token.is(VDMToken.PERIODIC)) { if (dialect != Dialect.VDM_RT) { throwMessage(2316, "Periodic threads only available in VDM-RT"); } nextToken(); checkFor(VDMToken.BRA, 2112, "Expecting '(' after periodic"); List<PExp> args = getExpressionReader().readExpressionList(); checkFor(VDMToken.KET, 2113, "Expecting ')' after periodic arguments"); checkFor(VDMToken.BRA, 2114, "Expecting '(' after periodic(...)"); LexNameToken name = readNameToken("Expecting (name) after periodic(...)"); checkFor(VDMToken.KET, 2115, "Expecting (name) after periodic(...)"); // PStm statement = AstFactory.newAPeriodicStm(token.location, name, args); return AstFactory.newPeriodicAThreadDefinition(name, args); } else if (token.is(VDMToken.SPORADIC)) { if (dialect != Dialect.VDM_RT) { throwMessage(2317, "Sporadic threads only available in VDM-RT"); } nextToken(); checkFor(VDMToken.BRA, 2312, "Expecting '(' after sporadic"); List<PExp> args = getExpressionReader().readExpressionList(); checkFor(VDMToken.KET, 2313, "Expecting ')' after sporadic arguments"); checkFor(VDMToken.BRA, 2314, "Expecting '(' after sporadic(...)"); LexNameToken name = readNameToken("Expecting (name) after sporadic(...)"); checkFor(VDMToken.KET, 2315, "Expecting (name) after sporadic(...)"); return AstFactory.newSporadicAThreadDefinition(name, args); } else { PStm stmt = getStatementReader().readStatement(); return AstFactory.newAThreadDefinition(stmt); } } private PDefinition readPermissionPredicateDefinition() throws LexException, ParserException { LexToken token = lastToken(); switch (token.type) { case PER: nextToken(); LexNameToken name = readNameToken("Expecting name after 'per'"); checkFor(VDMToken.IMPLIES, 2116, "Expecting <name> => <exp>"); PExp exp = getExpressionReader().readPerExpression(); return AstFactory.newAPerSyncDefinition(token.location, name, exp); case MUTEX: nextToken(); checkFor(VDMToken.BRA, 2117, "Expecting '(' after mutex"); LexNameList opnames = new LexNameList(); switch (lastToken().type) { case ALL: nextToken(); checkFor(VDMToken.KET, 2118, "Expecting ')' after 'all'"); break; default: LexNameToken op = readNameToken("Expecting a name"); opnames.add(op); while (ignore(VDMToken.COMMA)) { op = readNameToken("Expecting a name"); opnames.add(op); } checkFor(VDMToken.KET, 2119, "Expecting ')'"); break; } return AstFactory.newAMutexSyncDefinition(token.location, opnames); default: throwMessage(2028, "Expecting 'per' or 'mutex'"); return null; } } private PDefinition readNamedTraceDefinition() throws ParserException, LexException { ILexLocation start = lastToken().location; List<String> names = readTraceIdentifierList(); checkFor(VDMToken.COLON, 2264, "Expecting ':' after trace name(s)"); List<ATraceDefinitionTerm> traces = readTraceDefinitionList(); return AstFactory.newANamedTraceDefinition(start, names, traces); } private List<String> readTraceIdentifierList() throws ParserException, LexException { List<String> names = new Vector<String>(); names.add(readIdToken("Expecting trace identifier").getName()); while (lastToken().is(VDMToken.DIVIDE)) { nextToken(); names.add(readIdToken("Expecting trace identifier").getName()); } return names; } private List<ATraceDefinitionTerm> readTraceDefinitionList() throws LexException, ParserException { List<ATraceDefinitionTerm> list = new Vector<ATraceDefinitionTerm>(); list.add(readTraceDefinitionTerm()); while (lastToken().is(VDMToken.SEMICOLON)) { try { reader.push(); nextToken(); list.add(readTraceDefinitionTerm()); reader.unpush(); } catch (ParserException e) { reader.pop(); break; } } return list; } private ATraceDefinitionTerm readTraceDefinitionTerm() throws LexException, ParserException { List<PTraceDefinition> term = new Vector<PTraceDefinition>(); term.add(readTraceDefinition()); while (lastToken().is(VDMToken.PIPE)) { nextToken(); term.add(readTraceDefinition()); } return AstFactory.newATraceDefinitionTerm(term); } private PTraceDefinition readTraceDefinition() throws LexException, ParserException { if (lastToken().is(VDMToken.LET)) { return readTraceBinding(); } else { return readTraceRepeat(); } } private PTraceDefinition readTraceRepeat() throws ParserException, LexException { PTraceCoreDefinition core = readCoreTraceDefinition(); long from = 1; long to = 1; LexToken token = lastToken(); switch (token.type) { case TIMES: from = 0; to = Properties.traces_max_repeats; nextToken(); break; case PLUS: from = 1; to = Properties.traces_max_repeats; nextToken(); break; case QMARK: from = 0; to = 1; nextToken(); break; case SET_OPEN: if (nextToken().isNot(VDMToken.NUMBER)) { throwMessage(2266, "Expecting '{n}' or '{n1, n2}' after trace definition"); } LexIntegerToken lit = (LexIntegerToken) lastToken(); from = lit.value; to = lit.value; switch (nextToken().type) { case COMMA: if (nextToken().isNot(VDMToken.NUMBER)) { throwMessage(2265, "Expecting '{n1, n2}' after trace definition"); } lit = (LexIntegerToken) readToken(); to = lit.value; checkFor(VDMToken.SET_CLOSE, 2265, "Expecting '{n1, n2}' after trace definition"); break; case SET_CLOSE: nextToken(); break; default: throwMessage(2266, "Expecting '{n}' or '{n1, n2}' after trace definition"); } break; default: break; } return AstFactory.newARepeatTraceDefinition(token.location, core, from, to); } private PTraceDefinition readTraceBinding() throws ParserException, LexException { checkFor(VDMToken.LET, 2230, "Expecting 'let'"); ParserException letDefError = null; try { reader.push(); PTraceDefinition def = readLetDefBinding(); reader.unpush(); return def; } catch (ParserException e) { e.adjustDepth(reader.getTokensRead()); reader.pop(); letDefError = e; } try { reader.push(); PTraceDefinition def = readLetBeStBinding(); reader.unpush(); return def; } catch (ParserException e) { e.adjustDepth(reader.getTokensRead()); reader.pop(); throw e.deeperThan(letDefError) ? e : letDefError; } } private PTraceDefinition readLetDefBinding() throws ParserException, LexException { List<AValueDefinition> localDefs = new Vector<AValueDefinition>(); LexToken start = lastToken(); PDefinition def = readLocalDefinition(NameScope.LOCAL); if (!(def instanceof AValueDefinition)) { throwMessage(2270, "Only value definitions allowed in traces"); } localDefs.add((AValueDefinition) def); while (ignore(VDMToken.COMMA)) { def = readLocalDefinition(NameScope.LOCAL); if (!(def instanceof AValueDefinition)) { throwMessage(2270, "Only value definitions allowed in traces"); } localDefs.add((AValueDefinition) def); } checkFor(VDMToken.IN, 2231, "Expecting 'in' after local definitions"); PTraceDefinition body = readTraceDefinition(); return AstFactory.newALetDefBindingTraceDefinition(start.location, localDefs, body); } private PTraceDefinition readLetBeStBinding() throws ParserException, LexException { LexToken start = lastToken(); PMultipleBind bind = getBindReader().readMultipleBind(); PExp stexp = null; if (lastToken().is(VDMToken.BE)) { nextToken(); checkFor(VDMToken.ST, 2232, "Expecting 'st' after 'be' in let statement"); stexp = getExpressionReader().readExpression(); } checkFor(VDMToken.IN, 2233, "Expecting 'in' after bind in let statement"); PTraceDefinition body = readTraceDefinition(); return AstFactory.newALetBeStBindingTraceDefinition(start.location, bind, stexp, body); } private PTraceCoreDefinition readCoreTraceDefinition() throws ParserException, LexException { LexToken token = lastToken(); switch (token.type) { case IDENTIFIER: case NAME: case SELF: StatementReader sr = getStatementReader(); PStm stmt = sr.readCallStatement(); if (!(stmt instanceof ACallStm) && !(stmt instanceof ACallObjectStm)) { throwMessage(2267, "Expecting 'obj.op(args)' or 'op(args)'", token); } return AstFactory.newAApplyExpressionTraceCoreDefinition(stmt, getCurrentModule()); case BRA: nextToken(); List<ATraceDefinitionTerm> list = readTraceDefinitionList(); checkFor(VDMToken.KET, 2269, "Expecting '(trace definitions)'"); return AstFactory.newABracketedExpressionTraceCoreDefinition(token.location, list); case PIPEPIPE: nextToken(); checkFor(VDMToken.BRA, 2292, "Expecting '|| (...)'"); List<PTraceDefinition> defs = new Vector<PTraceDefinition>(); defs.add(readTraceDefinition()); checkFor(VDMToken.COMMA, 2293, "Expecting '|| (a, b {,...})'"); defs.add(readTraceDefinition()); while (lastToken().is(VDMToken.COMMA)) { nextToken(); defs.add(readTraceDefinition()); } checkFor(VDMToken.KET, 2294, "Expecting ')' ending || clause"); return AstFactory.newAConcurrentExpressionTraceCoreDefinition(token.location, defs); default: throwMessage(2267, "Expecting 'obj.op(args)' or 'op(args)'", token); return null; } } private PExp readFunctionBody() throws LexException, ParserException { ILexToken token = lastToken(); if (token.is(VDMToken.IS)) { switch (nextToken().type) { case NOT: nextToken(); checkFor(VDMToken.YET, 2125, "Expecting 'is not yet specified'"); checkFor(VDMToken.SPECIFIED, 2126, "Expecting 'is not yet specified'"); return AstFactory.newANotYetSpecifiedExp(token.getLocation()); case SUBCLASS: nextToken(); checkFor(VDMToken.RESPONSIBILITY, 2127, "Expecting 'is subclass responsibility'"); return AstFactory.newASubclassResponsibilityExp(token.getLocation()); default: if (dialect == Dialect.VDM_PP) { throwMessage(2033, "Expecting 'is not yet specified' or 'is subclass responsibility'", token); } else { throwMessage(2033, "Expecting 'is not yet specified'", token); } return null; } } else { ExpressionReader expr = getExpressionReader(); return expr.readExpression(); } } private PStm readOperationBody() throws LexException, ParserException { ILexToken token = lastToken(); if (token.is(VDMToken.IS)) { switch (nextToken().type) { case NOT: nextToken(); checkFor(VDMToken.YET, 2187, "Expecting 'is not yet specified"); checkFor(VDMToken.SPECIFIED, 2188, "Expecting 'is not yet specified"); return AstFactory.newANotYetSpecifiedStm(token.getLocation()); case SUBCLASS: nextToken(); checkFor(VDMToken.RESPONSIBILITY, 2189, "Expecting 'is subclass responsibility'"); return AstFactory.newASubclassResponsibilityStm(token.getLocation()); default: if (dialect == Dialect.VDM_PP) { throwMessage(2062, "Expecting 'is not yet specified' or 'is subclass responsibility'", token); } else { throwMessage(2062, "Expecting 'is not yet specified'", token); } return null; } } else { StatementReader stmt = getStatementReader(); return stmt.readStatement(); } } }