All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fujitsu.vdmj.syntax.DefinitionReader Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *
 *	Copyright (c) 2016 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 .
 *	SPDX-License-Identifier: GPL-3.0-or-later
 *
 ******************************************************************************/

package com.fujitsu.vdmj.syntax;

import java.util.List;
import java.util.Vector;
import java.util.Arrays;

import com.fujitsu.vdmj.Release;
import com.fujitsu.vdmj.Settings;
import com.fujitsu.vdmj.ast.annotations.ASTAnnotationList;
import com.fujitsu.vdmj.ast.definitions.ASTAccessSpecifier;
import com.fujitsu.vdmj.ast.definitions.ASTAssignmentDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTClassInvariantDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTDefinitionList;
import com.fujitsu.vdmj.ast.definitions.ASTEqualsDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTExplicitFunctionDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTExplicitOperationDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTImplicitFunctionDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTImplicitOperationDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTInstanceVariableDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTMutexSyncDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTNamedTraceDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTPerSyncDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTStateDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTThreadDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTTypeDefinition;
import com.fujitsu.vdmj.ast.definitions.ASTValueDefinition;
import com.fujitsu.vdmj.ast.expressions.ASTEqualsExpression;
import com.fujitsu.vdmj.ast.expressions.ASTExpression;
import com.fujitsu.vdmj.ast.expressions.ASTExpressionList;
import com.fujitsu.vdmj.ast.expressions.ASTNotYetSpecifiedExpression;
import com.fujitsu.vdmj.ast.expressions.ASTSubclassResponsibilityExpression;
import com.fujitsu.vdmj.ast.lex.LexCommentList;
import com.fujitsu.vdmj.ast.lex.LexIdentifierToken;
import com.fujitsu.vdmj.ast.lex.LexIntegerToken;
import com.fujitsu.vdmj.ast.lex.LexNameList;
import com.fujitsu.vdmj.ast.lex.LexNameToken;
import com.fujitsu.vdmj.ast.lex.LexToken;
import com.fujitsu.vdmj.ast.patterns.ASTBind;
import com.fujitsu.vdmj.ast.patterns.ASTIdentifierPattern;
import com.fujitsu.vdmj.ast.patterns.ASTMultipleBind;
import com.fujitsu.vdmj.ast.patterns.ASTPattern;
import com.fujitsu.vdmj.ast.patterns.ASTPatternList;
import com.fujitsu.vdmj.ast.patterns.ASTPatternListList;
import com.fujitsu.vdmj.ast.patterns.ASTSeqBind;
import com.fujitsu.vdmj.ast.patterns.ASTSetBind;
import com.fujitsu.vdmj.ast.patterns.ASTTuplePattern;
import com.fujitsu.vdmj.ast.patterns.ASTTypeBind;
import com.fujitsu.vdmj.ast.statements.ASTCallObjectStatement;
import com.fujitsu.vdmj.ast.statements.ASTCallStatement;
import com.fujitsu.vdmj.ast.statements.ASTErrorCase;
import com.fujitsu.vdmj.ast.statements.ASTErrorCaseList;
import com.fujitsu.vdmj.ast.statements.ASTExternalClause;
import com.fujitsu.vdmj.ast.statements.ASTExternalClauseList;
import com.fujitsu.vdmj.ast.statements.ASTNotYetSpecifiedStatement;
import com.fujitsu.vdmj.ast.statements.ASTPeriodicStatement;
import com.fujitsu.vdmj.ast.statements.ASTSpecificationStatement;
import com.fujitsu.vdmj.ast.statements.ASTSporadicStatement;
import com.fujitsu.vdmj.ast.statements.ASTStatement;
import com.fujitsu.vdmj.ast.statements.ASTSubclassResponsibilityStatement;
import com.fujitsu.vdmj.ast.traces.ASTTraceApplyExpression;
import com.fujitsu.vdmj.ast.traces.ASTTraceBracketedExpression;
import com.fujitsu.vdmj.ast.traces.ASTTraceConcurrentExpression;
import com.fujitsu.vdmj.ast.traces.ASTTraceCoreDefinition;
import com.fujitsu.vdmj.ast.traces.ASTTraceDefinition;
import com.fujitsu.vdmj.ast.traces.ASTTraceDefinitionList;
import com.fujitsu.vdmj.ast.traces.ASTTraceDefinitionTerm;
import com.fujitsu.vdmj.ast.traces.ASTTraceDefinitionTermList;
import com.fujitsu.vdmj.ast.traces.ASTTraceLetBeStBinding;
import com.fujitsu.vdmj.ast.traces.ASTTraceLetDefBinding;
import com.fujitsu.vdmj.ast.traces.ASTTraceRepeatDefinition;
import com.fujitsu.vdmj.ast.types.ASTFieldList;
import com.fujitsu.vdmj.ast.types.ASTFunctionType;
import com.fujitsu.vdmj.ast.types.ASTInvariantType;
import com.fujitsu.vdmj.ast.types.ASTNamedType;
import com.fujitsu.vdmj.ast.types.ASTOperationType;
import com.fujitsu.vdmj.ast.types.ASTParameterType;
import com.fujitsu.vdmj.ast.types.ASTPatternListTypePair;
import com.fujitsu.vdmj.ast.types.ASTPatternListTypePairList;
import com.fujitsu.vdmj.ast.types.ASTPatternTypePair;
import com.fujitsu.vdmj.ast.types.ASTProductType;
import com.fujitsu.vdmj.ast.types.ASTRecordType;
import com.fujitsu.vdmj.ast.types.ASTType;
import com.fujitsu.vdmj.ast.types.ASTTypeList;
import com.fujitsu.vdmj.ast.types.ASTUnresolvedType;
import com.fujitsu.vdmj.config.Properties;
import com.fujitsu.vdmj.lex.Dialect;
import com.fujitsu.vdmj.lex.LexException;
import com.fujitsu.vdmj.lex.LexLocation;
import com.fujitsu.vdmj.lex.LexTokenReader;
import com.fujitsu.vdmj.lex.Token;
import com.fujitsu.vdmj.messages.LocatedException;
import com.fujitsu.vdmj.typechecker.NameScope;

/**
 * A syntax analyser to parse definitions.
 */
public class DefinitionReader extends SyntaxReader
{
	public DefinitionReader(LexTokenReader reader)
	{
		super(reader);
	}

	private static Token[] sectionArray =
	{
		Token.TYPES,
		Token.FUNCTIONS,
		Token.STATE,
		Token.VALUES,
		Token.OPERATIONS,
		Token.INSTANCE,
		Token.THREAD,
		Token.SYNC,
		Token.TRACES,
		Token.END,
		Token.EOF
	};

	private static Token[] afterArray =
	{
		Token.SEMICOLON
	};

	private static List sectionList = Arrays.asList(sectionArray);

	private boolean newSection() throws LexException
	{
		return newSection(lastToken());
	}

	public static boolean newSection(LexToken tok)
	{
		return sectionList.contains(tok.type);
	}
	
	private boolean accessSpecifier() throws LexException
	{
		LexToken tok = lastToken();
		
		return tok.is(Token.PUBLIC) || tok.is(Token.PRIVATE) || tok.is(Token.PROTECTED) ||
			   tok.is(Token.PURE) || tok.is(Token.STATIC)|| tok.is(Token.ASYNC); 
	}

	public ASTDefinitionList readDefinitions() throws ParserException, LexException
	{
		ASTDefinitionList list = new ASTDefinitionList();
		boolean threadDone = false;

		while (lastToken().isNot(Token.EOF) && lastToken().isNot(Token.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(Token.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(Token.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 ASTAccessSpecifier readAccessSpecifier(boolean asyncOK, boolean pureOK)
		throws LexException, ParserException
	{
		if (dialect == Dialect.VDM_SL)
		{
			if (lastToken().is(Token.PURE))
			{
				if (Settings.release == Release.CLASSIC)
				{
					throwMessage(2325, "Pure operations are not available in classic");
				}
				
				if (pureOK)
				{
					nextToken();
					return new ASTAccessSpecifier(false, false, Token.PRIVATE, true);
				}
				else
				{
					throwMessage(2324, "Pure only permitted for operations");
				}
			}
			else
			{
				return ASTAccessSpecifier.DEFAULT;
			}
		}

		// Keyword counts
		int numStatic = 0;
		int numAsync = 0;
		int numPure = 0;
		int numAccess = 0;

		// Defaults
		boolean isStatic = false;
		boolean isAsync = false;
		boolean isPure = false;
		Token access = Token.PRIVATE;
		
		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:
				case PRIVATE:
				case PROTECTED:
					if (++numAccess > 1)
					{
						throwMessage(2329, "Duplicate access specifier keyword");
					}

					access = lastToken().type;
					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 new ASTAccessSpecifier(isStatic, isAsync, access, isPure);
	}
	
	public ASTTypeDefinition readTypeDefinition() throws ParserException, LexException
	{
		LexIdentifierToken id = readIdToken("Expecting new type identifier");
		TypeReader tr = getTypeReader();
		ASTInvariantType invtype = null;

		switch (lastToken().type)
		{
			case EQUALS:
				nextToken();
				ASTNamedType nt = new ASTNamedType(idToName(id), tr.readType());

				if (nt.type instanceof ASTUnresolvedType &&
					((ASTUnresolvedType)nt.type).typename.equals(nt.typename))
				{
					throwMessage(2014, "Recursive type declaration");
				}

				invtype = nt;
				break;

			case COLONCOLON:
				nextToken();
				invtype = new ASTRecordType(idToName(id), tr.readFieldList(), false);
				break;

			default:
				throwMessage(2015, "Expecting = or ::");
		}

		ASTPattern invPattern = null;
		ASTExpression invExpression = null;
		ASTPattern eqPattern1 = null;
		ASTPattern eqPattern2 = null;
		ASTExpression eqExpression = null;
		ASTPattern ordPattern1 = null;
		ASTPattern ordPattern2 = null;
		ASTExpression ordExpression = null;

		while (lastToken().is(Token.INV) || lastToken().is(Token.EQ) || lastToken().is(Token.ORD))
		{
    		switch (lastToken().type)
    		{
    			case INV:
    				if (invPattern != null)
    				{
    					throwMessage(2332, "Duplicate inv clause");
    				}
    				
    				if (Settings.strict && (eqPattern1 != null || ordPattern1 != null))
    				{
    					warning(5026, "Strict: Order should be inv, eq, ord", lastToken().location);
    				}
    				
        			nextToken();
        			invPattern = getPatternReader().readPattern();
        			checkFor(Token.EQUALSEQUALS, 2087, "Expecting '==' after pattern in invariant");
        			invExpression = getExpressionReader().readExpression();
        			break;
        			
    			case EQ:
    				if (Settings.release == Release.CLASSIC)
    				{
    					throwMessage(2333, "Type eq/ord clauses not available in classic");
    				}

    				if (eqPattern1 != null)
    				{
    					throwMessage(2332, "Duplicate eq clause");
    				}
    				
    				if (Settings.strict && ordPattern1 != null)
    				{
    					warning(5026, "Strict: order should be inv, eq, ord", lastToken().location);
    				}
    				
        			nextToken();
        			eqPattern1 = getPatternReader().readPattern();
        			checkFor(Token.EQUALS, 2087, "Expecting '=' between patterns in eq clause");
        			eqPattern2 = getPatternReader().readPattern();
        			checkFor(Token.EQUALSEQUALS, 2087, "Expecting '==' after patterns in eq clause");
        			eqExpression = getExpressionReader().readExpression();
    				break;
    				
    			case ORD:
    				if (Settings.release == Release.CLASSIC)
    				{
    					throwMessage(2333, "Type eq/ord clauses not available in classic");
    				}

    				if (ordPattern1 != null)
    				{
    					throwMessage(2332, "Duplicate ord clause");
    				}
    				
        			nextToken();
        			ordPattern1 = getPatternReader().readPattern();
        			checkFor(Token.LT, 2087, "Expecting '<' between patterns in ord clause");
        			ordPattern2 = getPatternReader().readPattern();
        			checkFor(Token.EQUALSEQUALS, 2087, "Expecting '==' after patterns in ord clause");
        			ordExpression = getExpressionReader().readExpression();
    				break;

    			default:
    				throwMessage(2331, "Expecting inv, eq or ord clause");
    		}
		}

		return new ASTTypeDefinition(idToName(id), invtype, invPattern, invExpression,
			eqPattern1, eqPattern2, eqExpression, ordPattern1, ordPattern2, ordExpression);
	}

	private ASTDefinitionList readTypes() throws LexException, ParserException
	{
		checkFor(Token.TYPES, 2013, "Expected 'types'");
		ASTDefinitionList list = new ASTDefinitionList();

		while (!newSection())
		{
			try
			{
				LexCommentList comments = getComments();
				ASTAnnotationList annotations = readAnnotations(comments);
				annotations.astBefore(this);
				ASTAccessSpecifier access = readAccessSpecifier(false, false);
				ASTTypeDefinition def = readTypeDefinition();
				annotations.astAfter(this, def);
				def.setAnnotations(annotations);
				def.setComments(comments);

				// Force all type defs (invs) to be static
				def.setAccessSpecifier(access.getStatic(true));
				list.add(def);

				if (!newSection())
				{
					checkFor(Token.SEMICOLON,
						2078, "Missing ';' after type definition");
				}
			}
			catch (LocatedException e)
			{
				report(e, afterArray, sectionArray);
			}
		}

		return list;
	}

	private ASTDefinitionList readValues() throws LexException, ParserException
	{
		checkFor(Token.VALUES, 2013, "Expected 'values'");
		ASTDefinitionList list = new ASTDefinitionList();

		while (!newSection())
		{
			try
			{
				LexCommentList comments = getComments();
				ASTAnnotationList annotations = readAnnotations(comments);
				annotations.astBefore(this);
				ASTAccessSpecifier access = readAccessSpecifier(false, false);
				ASTDefinition def = readValueDefinition(NameScope.GLOBAL);
				annotations.astAfter(this, def);
				def.setAnnotations(annotations);
				def.setComments(comments);

				// Force all values to be static
				def.setAccessSpecifier(access.getStatic(true));
				list.add(def);

				if (!newSection())
				{
					checkFor(Token.SEMICOLON,
						2081, "Missing ';' after value definition");
				}
			}
			catch (LocatedException e)
			{
				report(e, afterArray, sectionArray);
			}
		}

		return list;
	}

	private ASTDefinitionList readFunctions() throws LexException, ParserException
	{
		checkFor(Token.FUNCTIONS, 2013, "Expected 'functions'");
		ASTDefinitionList list = new ASTDefinitionList();

		while (!newSection())
		{
			try
			{
				LexCommentList comments = getComments();
				ASTAnnotationList annotations = readAnnotations(comments);
				annotations.astBefore(this);
				ASTAccessSpecifier access = readAccessSpecifier(false, false);
				ASTDefinition def = readFunctionDefinition();
				annotations.astAfter(this, def);
				def.setAnnotations(annotations);
				def.setComments(comments);

				if (Settings.release == Release.VDM_10)
				{
					// Force all functions to be static for VDM-10
					def.setAccessSpecifier(access.getStatic(true));
				}
				else
				{
					def.setAccessSpecifier(access);
				}

				list.add(def);

				if (!newSection())
				{
					checkFor(Token.SEMICOLON,
						2079, "Missing ';' after function definition");
				}
			}
			catch (LocatedException e)
			{
				report(e, afterArray, sectionArray);
			}
		}

		return list;
	}

	public ASTDefinitionList readOperations() throws LexException, ParserException
	{
		checkFor(Token.OPERATIONS, 2013, "Expected 'operations'");
		ASTDefinitionList list = new ASTDefinitionList();

		while (!newSection())
		{
			try
			{
				LexCommentList comments = getComments();
				ASTAnnotationList annotations = readAnnotations(comments);
				annotations.astBefore(this);
				ASTAccessSpecifier access = readAccessSpecifier(dialect == Dialect.VDM_RT, true);
				ASTDefinition def = readOperationDefinition();
				annotations.astAfter(this, def);
				def.setAccessSpecifier(access);
				def.setAnnotations(annotations);
				def.setComments(comments);
				list.add(def);

				if (!newSection())
				{
					LexToken end = lastToken();
					
					checkFor(Token.SEMICOLON,
						2082, "Missing ';' after operation definition");
					
					if (lastToken().isNot(Token.IDENTIFIER) && !newSection() && !accessSpecifier())
					{
						throwMessage(2082, "Semi-colon is not allowed here", end);
					}
				}
			}
			catch (LocatedException e)
			{
				report(e, afterArray, sectionArray);
			}
		}

		return list;
	}

	public ASTDefinitionList readInstanceVariables() throws LexException, ParserException
	{
		checkFor(Token.INSTANCE, 2083, "Expected 'instance variables'");
		checkFor(Token.VARIABLES, 2083, "Expecting 'instance variables'");
		ASTDefinitionList list = new ASTDefinitionList();

		while (!newSection())
		{
			try
			{
				LexCommentList comments = getComments();
				ASTAnnotationList annotations = readAnnotations(comments);
				annotations.astBefore(this);
				ASTDefinition def = readInstanceVariableDefinition();
				annotations.astAfter(this, def);
				def.setAnnotations(annotations);
				def.setComments(comments);
				list.add(def);

				if (!newSection())
				{
					checkFor(Token.SEMICOLON,
						2084, "Missing ';' after instance variable definition");
				}
			}
			catch (LocatedException e)
			{
				report(e, afterArray, sectionArray);
			}
		}

		return list;
	}

	private ASTDefinitionList readTraces() throws LexException, ParserException
	{
		checkFor(Token.TRACES, 2013, "Expected 'traces'");
		ASTDefinitionList list = new ASTDefinitionList();

		while (!newSection())
		{
			try
			{
				LexCommentList comments = getComments();
				ASTAnnotationList annotations = readAnnotations(comments);
				annotations.astBefore(this);
				ASTDefinition def = readNamedTraceDefinition();
				annotations.astAfter(this, def);
				def.setAnnotations(annotations);
				def.setComments(comments);
				list.add(def);

				if (!ignore(Token.SEMICOLON) && Settings.strict)
				{
					if (!newSection())
					{
						warning(5028, "Strict: expecting semi-colon between traces", lastToken().location);
					}
				}
			}
			catch (LocatedException e)
			{
				report(e, afterArray, sectionArray);
			}
		}

		return list;
	}

	private ASTDefinitionList readSyncs() throws LexException, ParserException
	{
		checkFor(Token.SYNC, 2013, "Expected 'sync'");
		ASTDefinitionList list = new ASTDefinitionList();

		while (!newSection())
		{
			try
			{
				LexCommentList comments = getComments();
				ASTAnnotationList annotations = readAnnotations(comments);
				annotations.astBefore(this);
				ASTDefinition def = readPermissionPredicateDefinition();
				annotations.astAfter(this, def);
				list.add(def);

				if (!newSection())
				{
					checkFor(Token.SEMICOLON,
						2086, "Missing ';' after sync definition");
				}
			}
			catch (LocatedException e)
			{
				report(e, afterArray, sectionArray);
			}
		}

		return list;
	}

	public ASTTypeList readTypeParams() throws LexException, ParserException
	{
		ASTTypeList typeParams = null;

		if (lastToken().is(Token.SEQ_OPEN))
		{
			typeParams = new ASTTypeList();
			nextToken();
			checkFor(Token.AT, 2088, "Expecting '@' before type parameter");
			LexIdentifierToken tid = readIdToken("Expecting '@identifier' in type parameter list");
			typeParams.add(new ASTParameterType(idToName(tid)));

			while (ignore(Token.COMMA))
			{
				checkFor(Token.AT, 2089, "Expecting '@' before type parameter");
				tid = readIdToken("Expecting '@identifier' in type parameter list");
				typeParams.add(new ASTParameterType(idToName(tid)));
			}

			checkFor(Token.SEQ_CLOSE, 2090, "Expecting ']' after type parameters");
		}

		return typeParams;
	}
	
	private void verifyName(String name) throws ParserException, LexException
	{
		if (name.startsWith("mk_"))
		{
			throwMessage(2016, "Name cannot start with 'mk_'");
		}
		else if (name.equals("mu"))
		{
			throwMessage(2016, "Name cannot be 'mu' (reserved)");
		}
		else if (name.equals("narrow_"))
		{
			throwMessage(2016, "Name cannot be 'narrow_' (reserved)");
		}
	}

	private ASTDefinition readFunctionDefinition() throws ParserException, LexException
	{
		ASTDefinition def = null;
		LexIdentifierToken funcName = readIdToken("Expecting new function identifier");
		verifyName(funcName.name);

		ASTTypeList typeParams = readTypeParams();

		if (lastToken().is(Token.COLON))
		{
			def = readExplicitFunctionDefinition(funcName, typeParams);
		}
		else if (lastToken().is(Token.BRA))
		{
			def = readImplicitFunctionDefinition(funcName, typeParams);
		}
		else
		{
			throwMessage(2017, "Expecting ':' or '(' after name in function definition");
		}

		LexLocation.addSpan(idToName(funcName), lastToken());
		return def;
	}

	private ASTDefinition readExplicitFunctionDefinition(LexIdentifierToken funcName, ASTTypeList typeParams)
		throws ParserException, LexException
	{
		// Explicit function definition, like "f: int->bool f(x) == true"

		nextToken();
		ASTType t = getTypeReader().readType();

		if (!(t instanceof ASTFunctionType))
		{
			throwMessage(2018, "Function type is not a -> or +> function");
		}

		ASTFunctionType type = (ASTFunctionType)t;

		LexIdentifierToken name =
			readIdToken("Expecting identifier after function type in definition");

		if (!name.equals(funcName))
		{
			throwMessage(2019, "Expecting identifier " + funcName.name + " after type in definition", name);
		}

		if (lastToken().isNot(Token.BRA))
		{
			throwMessage(2020, "Expecting '(' after function name");
		}

		ASTPatternListList parameters = new ASTPatternListList();

		while (lastToken().is(Token.BRA))
		{
			if (nextToken().isNot(Token.KET))
			{
    			parameters.add(getPatternReader().readPatternList());
    			checkFor(Token.KET, 2091, "Expecting ')' after function parameters");
    		}
    		else
    		{
    			parameters.add(new ASTPatternList());	// empty "()"
    			nextToken();
    		}
		}

		checkFor(Token.EQUALSEQUALS, 2092, "Expecting '==' after parameters");
		ExpressionReader expr = getExpressionReader();
		ASTExpression body = readFunctionBody();
		ASTExpression precondition = null;
		ASTExpression postcondition = null;
		ASTExpression measure = null;

		if (lastToken().is(Token.PRE))
		{
			nextToken();
			precondition = expr.readExpression();
		}

		if (lastToken().is(Token.POST))
		{
			nextToken();
			postcondition = expr.readExpression();
		}

		if (lastToken().is(Token.MEASURE))
		{
			nextToken();
			
			if (lastToken().is(Token.IS))
			{
				nextToken();
				checkFor(Token.NOT, 2125, "Expecting 'is not yet specified'");
				checkFor(Token.YET, 2125, "Expecting 'is not yet specified'");
				checkFor(Token.SPECIFIED, 2126, "Expecting 'is not yet specified'");
				measure = new ASTNotYetSpecifiedExpression(lastToken().location);
			}
			else
			{
				measure = getExpressionReader().readExpression();
			}
		}

		return new ASTExplicitFunctionDefinition(
			idToName(funcName), typeParams, type,
			parameters, body, precondition, postcondition, false,
			measure);
	}

	private ASTDefinition readImplicitFunctionDefinition(LexIdentifierToken funcName, ASTTypeList typeParams)
		throws ParserException, LexException
	{
		// Implicit, like g(x: int) y: bool pre exp post exp

		nextToken();

		PatternReader pr = getPatternReader();
		TypeReader tr = getTypeReader();
		ASTPatternListTypePairList parameterPatterns = new ASTPatternListTypePairList();

		if (lastToken().isNot(Token.KET))
		{
			ASTPatternList pl = pr.readPatternList();
			checkFor(Token.COLON, 2093, "Missing colon after pattern/type parameter");
			parameterPatterns.add(new ASTPatternListTypePair(pl, tr.readType()));

			while (ignore(Token.COMMA))
			{
				pl = pr.readPatternList();
				checkFor(Token.COLON, 2093, "Missing colon after pattern/type parameter");
				parameterPatterns.add(new ASTPatternListTypePair(pl, tr.readType()));
			}
		}

    	checkFor(Token.KET, 2124, "Expecting ')' after parameters");

		LexToken firstResult = lastToken();
   		ASTPatternList resultNames = new ASTPatternList();
   		ASTTypeList resultTypes = new ASTTypeList();

   		do
   		{
   			LexIdentifierToken rname = readIdToken("Expecting result identifier");
   	   		resultNames.add(new ASTIdentifierPattern(idToName(rname)));
   	   		checkFor(Token.COLON, 2094, "Missing colon in identifier/type return value");
   	   		resultTypes.add(tr.readType());
   		}
   		while (ignore(Token.COMMA));

   		if (lastToken().is(Token.IDENTIFIER))
		{
			throwMessage(2261, "Missing comma between return types?");
		}

   		ASTPatternTypePair resultPattern = null;

   		if (resultNames.size() > 1)
   		{
   			resultPattern = new ASTPatternTypePair(
   	   			new ASTTuplePattern(firstResult.location, resultNames),
 	   			new ASTProductType(firstResult.location, resultTypes));
   		}
   		else
   		{
   			resultPattern = new ASTPatternTypePair(
   	   			resultNames.get(0), resultTypes.get(0));
   		}

		ExpressionReader expr = getExpressionReader();
		ASTExpression body = null;
		ASTExpression precondition = null;
		ASTExpression postcondition = null;
		ASTExpression measure = null;

		if (lastToken().is(Token.EQUALSEQUALS))		// extended implicit function
		{
			nextToken();
			body = readFunctionBody();
		}

		if (lastToken().is(Token.PRE))
		{
			nextToken();
			precondition = expr.readExpression();
		}

		if (body == null)	// Mandatory for standard implicit functions
		{
			checkFor(Token.POST, 2095, "Implicit function must have post condition");
			postcondition = expr.readExpression();
		}
		else
		{
			if (lastToken().is(Token.POST))
			{
				nextToken();
				postcondition = expr.readExpression();
			}
		}

		if (lastToken().is(Token.MEASURE))
		{
			nextToken();
			
			if (lastToken().is(Token.IS))
			{
				nextToken();
				checkFor(Token.NOT, 2125, "Expecting 'is not yet specified'");
				checkFor(Token.YET, 2125, "Expecting 'is not yet specified'");
				checkFor(Token.SPECIFIED, 2126, "Expecting 'is not yet specified'");
				measure = new ASTNotYetSpecifiedExpression(lastToken().location);
			}
			else
			{
				measure = getExpressionReader().readExpression();
			}
		}

		return new ASTImplicitFunctionDefinition(
			idToName(funcName), typeParams, parameterPatterns, resultPattern, body,
			precondition, postcondition, measure);
	}

	public ASTDefinition readLocalDefinition() throws ParserException, LexException
	{
		ParserException funcDefError = null;

    	try
    	{
        	reader.push();
        	ASTDefinition def = readFunctionDefinition();
    		reader.unpush();
    		return def;
    	}
    	catch (ParserException e)		// Not a function then...
    	{
			e.adjustDepth(reader.getTokensRead());
    		reader.pop();
    		funcDefError = e;
    	}

		try
		{
        	reader.push();
        	ASTDefinition def = readValueDefinition(NameScope.LOCAL);
    		reader.unpush();
    		return def;
		}
		catch (ParserException e)
		{
			e.adjustDepth(reader.getTokensRead());
    		reader.pop();
			throw e.deeperThan(funcDefError) ? e : funcDefError;
		}
	}

	public ASTDefinition readValueDefinition(NameScope scope) throws ParserException, LexException
	{
       	// Should be [:]=

    	ASTPattern p = getPatternReader().readPattern();
    	ASTType type = null;

    	if (lastToken().is(Token.COLON))
    	{
    		nextToken();
    		type = getTypeReader().readType();
    	}

 		checkFor(Token.EQUALS, 2096, "Expecting [:]=");
		return new ASTValueDefinition(
			scope, p, type, getExpressionReader().readExpression());
	}

	private ASTDefinition readStateDefinition() throws ParserException, LexException
	{
		LexCommentList comments = getComments();
		ASTAnnotationList annotations = readAnnotations(comments);

		LexIdentifierToken name = readIdToken("Expecting identifier after 'state' definition");
		checkFor(Token.OF, 2097, "Expecting 'of' after state name");
		ASTFieldList fieldList = getTypeReader().readFieldList();

		ASTExpression invExpression = null;
		ASTExpression initExpression = null;
		ASTPattern invPattern = null;
		ASTPattern initPattern = null;

		if (lastToken().is(Token.INV))
		{
			nextToken();
			invPattern = getPatternReader().readPattern();
			checkFor(Token.EQUALSEQUALS, 2098, "Expecting '==' after pattern in invariant");
			invExpression = getExpressionReader().readExpression();
		}

		if (lastToken().is(Token.INIT))
		{
			nextToken();
			initPattern = getPatternReader().readPattern();
			checkFor(Token.EQUALSEQUALS, 2099, "Expecting '==' after pattern in initializer");
			initExpression = getExpressionReader().readExpression();
		}

		// Be forgiving about the inv/init order
		if (lastToken().is(Token.INV) && invExpression == null)
		{
			if (Settings.strict)
			{
				warning(5027, "Strict: order should be inv, init", lastToken().location);
			}
			
			nextToken();
			invPattern = getPatternReader().readPattern();
			checkFor(Token.EQUALSEQUALS, 2098, "Expecting '==' after pattern in invariant");
			invExpression = getExpressionReader().readExpression();
		}

		checkFor(Token.END, 2100, "Expecting 'end' after state definition");
		ASTStateDefinition def = new ASTStateDefinition(idToName(name), fieldList,
			invPattern, invExpression, initPattern, initExpression);
		def.setAnnotations(annotations);
		def.setComments(comments);
		return def;
	}

	private ASTDefinition readOperationDefinition()
		throws ParserException, LexException
	{
		ASTDefinition def = null;
		LexIdentifierToken opName = readIdToken("Expecting new operation identifier");
		verifyName(opName.name);

		if (lastToken().is(Token.COLON))
		{
			def = readExplicitOperationDefinition(opName);
		}
		else if (lastToken().is(Token.BRA))
		{
			def = readImplicitOperationDefinition(opName);
		}
		else if (lastToken().is(Token.SEQ_OPEN))
		{
			throwMessage(2059, "Operations cannot have [@T] type parameters");
		}
		else
		{
			throwMessage(2021, "Expecting ':' or '(' after name in operation definition");
		}

		LexLocation.addSpan(idToName(opName), lastToken());
		return def;
	}

	private ASTDefinition readExplicitOperationDefinition(LexIdentifierToken opName)
		throws ParserException, LexException
	{
		// Like "f: int ==> bool f(x) == "

		nextToken();
		ASTOperationType type = getTypeReader().readOperationType();

		LexIdentifierToken name =
			readIdToken("Expecting operation identifier after type in definition");

		if (!name.equals(opName))
		{
			throwMessage(2022, "Expecting name " + opName.name + " after type in definition", name);
		}

		if (lastToken().isNot(Token.BRA))
		{
			throwMessage(2023, "Expecting '(' after operation name");
		}

		ASTPatternList parameters = null;

		if (nextToken().isNot(Token.KET))
		{
			parameters = getPatternReader().readPatternList();
			checkFor(Token.KET, 2101, "Expecting ')' after operation parameters");
		}
		else
		{
			parameters = new ASTPatternList();		// empty "()"
			nextToken();
		}

		checkFor(Token.EQUALSEQUALS, 2102, "Expecting '==' after parameters");
		ASTStatement body = readOperationBody();
		ASTExpression precondition = null;
		ASTExpression postcondition = null;

		if (lastToken().is(Token.PRE))
		{
			nextToken();
			precondition = getExpressionReader().readExpression();
		}

		if (lastToken().is(Token.POST))
		{
			nextToken();
			postcondition = getExpressionReader().readExpression();
		}

		ASTExplicitOperationDefinition def = new ASTExplicitOperationDefinition(
			idToName(opName), type,
			parameters, precondition, postcondition, body);

		return def;
	}

	private ASTDefinition readImplicitOperationDefinition(LexIdentifierToken opName)
		throws ParserException, LexException
	{
		// Like g(x: int) [y: bool]? ext rd fred[:int] pre exp post exp

		nextToken();
		PatternReader pr = getPatternReader();
		TypeReader tr = getTypeReader();
		ASTPatternListTypePairList parameterPatterns = new ASTPatternListTypePairList();

		if (lastToken().isNot(Token.KET))
		{
			ASTPatternList pl = pr.readPatternList();
			checkFor(Token.COLON, 2103, "Missing colon after pattern/type parameter");
			parameterPatterns.add(new ASTPatternListTypePair(pl, tr.readType()));

			while (ignore(Token.COMMA))
			{
				pl = pr.readPatternList();
				checkFor(Token.COLON, 2103, "Missing colon after pattern/type parameter");
				parameterPatterns.add(new ASTPatternListTypePair(pl, tr.readType()));
			}
		}

    	checkFor(Token.KET, 2124, "Expecting ')' after args");

		LexToken firstResult = lastToken();
   		ASTPatternTypePair resultPattern = null;

		if (firstResult.is(Token.IDENTIFIER))
		{
			ASTPatternList resultNames = new ASTPatternList();
			ASTTypeList resultTypes = new ASTTypeList();

			do
			{
				LexIdentifierToken rname = readIdToken("Expecting result identifier");
				resultNames.add(new ASTIdentifierPattern(idToName(rname)));
				checkFor(Token.COLON, 2104, "Missing colon in identifier/type return value");
				resultTypes.add(tr.readType());
			}
			while (ignore(Token.COMMA));

			if (lastToken().is(Token.IDENTIFIER))
			{
				throwMessage(2261, "Missing comma between return types?");
			}

			if (resultNames.size() > 1)
			{
				resultPattern = new ASTPatternTypePair(
					new ASTTuplePattern(firstResult.location, resultNames),
					new ASTProductType(firstResult.location, resultTypes));
			}
			else
			{
				resultPattern = new ASTPatternTypePair(
					resultNames.get(0), resultTypes.get(0));
			}
		}

		ASTStatement body = null;

		if (lastToken().is(Token.EQUALSEQUALS))		// extended implicit operation
		{
			nextToken();
			body = readOperationBody();
		}

		ASTSpecificationStatement spec = readSpecification(opName.location, body == null);

		ASTImplicitOperationDefinition def = new ASTImplicitOperationDefinition(
			idToName(opName), parameterPatterns, resultPattern, body, spec);

		return def;
	}

	public ASTSpecificationStatement readSpecification(
		LexLocation location, boolean postMandatory)
		throws ParserException, LexException
	{
		ASTExternalClauseList externals = null;

		if (lastToken().is(Token.EXTERNAL))
		{
			externals = new ASTExternalClauseList();
			nextToken();

			while (lastToken().is(Token.READ) || lastToken().is(Token.WRITE))
			{
				externals.add(readExternal());
			}

			if (externals.isEmpty())
			{
				throwMessage(2024, "Expecting external declarations after 'ext'");
			}
		}

		ExpressionReader expr = getExpressionReader();
		ASTExpression precondition = null;
		ASTExpression postcondition = null;

		if (lastToken().is(Token.PRE))
		{
			nextToken();
			precondition = expr.readExpression();
		}

		if (postMandatory)	// Mandatory for standard implicit operations
		{
			checkFor(Token.POST, 2105, "Implicit operation must define a post condition");
			postcondition = expr.readExpression();
		}
		else
		{
			if (lastToken().is(Token.POST))
			{
				nextToken();
				postcondition = expr.readExpression();
			}
		}

		ASTErrorCaseList errors = null;

		if (lastToken().is(Token.ERRS))
		{
			errors = new ASTErrorCaseList();
			nextToken();

			while (lastToken() instanceof LexIdentifierToken)
			{
				LexIdentifierToken name = readIdToken("Expecting error identifier");
				checkFor(Token.COLON, 2106, "Expecting ':' after name in errs clause");
				ASTExpression left = expr.readExpression();
				checkFor(Token.ARROW, 2107, "Expecting '->' in errs clause");
				ASTExpression right = expr.readExpression();
				errors.add(new ASTErrorCase(name, left, right));
			}

			if (errors.isEmpty())
			{
				throwMessage(2025, "Expecting : exp->exp in errs clause");
			}
		}

		return new ASTSpecificationStatement(location,
						externals, precondition, postcondition, errors);
	}

	private ASTExternalClause readExternal() throws ParserException, LexException
	{
		LexToken mode = lastToken();

		if (mode.isNot(Token.READ) && mode.isNot(Token.WRITE))
		{
			throwMessage(2026, "Expecting 'rd' or 'wr' after 'ext'");
		}

		LexNameList names = new LexNameList();
		nextToken();
		names.add(readNameToken("Expecting name in external clause"));

		while (ignore(Token.COMMA))
		{
			names.add(readNameToken("Expecting name in external clause"));
		}

		ASTType type = null;

		if (lastToken().is(Token.COLON))
		{
			nextToken();
			type = getTypeReader().readType();
		}

		return new ASTExternalClause(mode, names, type);
	}

	public ASTEqualsDefinition readEqualsDefinition()
		throws ParserException, LexException
	{
       	// The grammar here says the form of the definition should be
		// "def" = "in" , but since
		// a set bind is "s in set S" that naively parses as
		// "s in set (S = )". Talking to PGL, we have to
		// make a special parse here. It is one of these forms:
		//
		//	"def"  "="  "in" ...
		//	"def"  "="  "in" ...
		//	"def"  "in set"  "in" ...
		//	"def"  "in seq"  "in" ...
		//
		// and the "=" is unpicked from the left and right of the equals
		// expression in the last two cases.

		LexLocation location = lastToken().location;
		ParserException equalsDefError = null;

    	try	// "def"  "="  "in" ...
    	{
        	reader.push();
    		ASTPattern pattern = getPatternReader().readPattern();
     		checkFor(Token.EQUALS, 2108, "Expecting =");
     		ASTExpression test = getExpressionReader().readExpression();
    		reader.unpush();

     		return new ASTEqualsDefinition(location, pattern, test);
    	}
    	catch (ParserException e)
    	{
			e.adjustDepth(reader.getTokensRead());
    		reader.pop();
    		equalsDefError = e;
    	}

		try	// "def"  "="  "in" ...
		{
        	reader.push();
    		ASTTypeBind typebind = getBindReader().readTypeBind();
     		checkFor(Token.EQUALS, 2109, "Expecting =");
     		ASTExpression test = getExpressionReader().readExpression();
    		reader.unpush();

     		return new ASTEqualsDefinition(location, typebind, test);
		}
		catch (ParserException e)
		{
			e.adjustDepth(reader.getTokensRead());
    		reader.pop();
			equalsDefError = e.deeperThan(equalsDefError) ? e : equalsDefError;
		}

		try
		{
        	reader.push();
    		ASTPattern pattern = getPatternReader().readPattern();
     		checkFor(Token.IN, 2110, "Expecting  in set|seq ");
     		ASTBind bind = null;
     		ASTEqualsExpression test = null;
     		
     		switch (lastToken().type)
     		{
     			case SET:			// "def"  "in set"  "in" ...
     				nextToken();
             		test = getExpressionReader().readDefEqualsExpression();
             		bind = new ASTSetBind(pattern, test.left);
             		reader.unpush();
             		return new ASTEqualsDefinition(location, bind, test.right);
             		
     			case SEQ:			// "def"  "in seq"  "in" ...
					if (Settings.release == Release.CLASSIC)
					{
						throwMessage(2328, "Sequence binds are not available in classic");
					}

					nextToken();
             		test = getExpressionReader().readDefEqualsExpression();
             		bind = new ASTSeqBind(pattern, test.left);
             		reader.unpush();
             		return new ASTEqualsDefinition(location, bind, test.right);
             		
     			default:
     				throwMessage(2111, "Expecting  in set|seq ");
     				return null;
     		}
		}
		catch (ParserException e)
		{
			e.adjustDepth(reader.getTokensRead());
    		reader.pop();
			throw e.deeperThan(equalsDefError) ? e : equalsDefError;
		}
 	}

	private ASTDefinition readInstanceVariableDefinition()
		throws ParserException, LexException
    {
		LexToken token = lastToken();

		if (token.is(Token.INV))
		{
			nextToken();
			ASTExpression exp = getExpressionReader().readExpression();
			String str = getCurrentModule();
			LexNameToken className = new LexNameToken(str, str, token.location);
			return new ASTClassInvariantDefinition(
				className.getInvName(token.location), exp);
		}
		else
		{
			LexCommentList comments = getComments();
			ASTAnnotationList annotations = readAnnotations(comments);
			ASTAccessSpecifier access = readAccessSpecifier(false, false);
			ASTAssignmentDefinition def = getStatementReader().readAssignmentDefinition();
			ASTInstanceVariableDefinition ivd =
				new ASTInstanceVariableDefinition(def.name, def.type, def.expression);
			ivd.setAccessSpecifier(access);
			ivd.setAnnotations(annotations);
			ivd.setComments(comments);

			return ivd;
		}
    }

	private ASTDefinition readThreadDefinition() throws LexException, ParserException
	{
		LexToken token = lastToken();

		if (token.is(Token.PERIODIC))
		{
			if (dialect != Dialect.VDM_RT)
			{
				throwMessage(2316, "Periodic threads only available in VDM-RT");
			}

			nextToken();
			checkFor(Token.BRA, 2112, "Expecting '(' after periodic");
			ASTExpressionList args = getExpressionReader().readExpressionList();
			checkFor(Token.KET, 2113, "Expecting ')' after periodic arguments");
			checkFor(Token.BRA, 2114, "Expecting '(' after periodic(...)");
			LexNameToken name = readNameToken("Expecting (name) after periodic(...)");
			checkFor(Token.KET, 2115, "Expecting (name) after periodic(...)");

			return new ASTThreadDefinition(new ASTPeriodicStatement(name, args));
		}
		else if (token.is(Token.SPORADIC))
		{
			if (dialect != Dialect.VDM_RT)
			{
				throwMessage(2317, "Sporadic threads only available in VDM-RT");
			}

			nextToken();
			checkFor(Token.BRA, 2312, "Expecting '(' after sporadic");
			ASTExpressionList args = getExpressionReader().readExpressionList();
			checkFor(Token.KET, 2313, "Expecting ')' after sporadic arguments");
			checkFor(Token.BRA, 2314, "Expecting '(' after sporadic(...)");
			LexNameToken name = readNameToken("Expecting (name) after sporadic(...)");
			checkFor(Token.KET, 2315, "Expecting (name) after sporadic(...)");

			return new ASTThreadDefinition(new ASTSporadicStatement(name, args));
		}
		else
		{
			ASTStatement stmt = getStatementReader().readStatement();
			return new ASTThreadDefinition(stmt);
		}
	}

	private ASTDefinition readPermissionPredicateDefinition()
		throws LexException, ParserException
	{
		LexToken token = lastToken();

		switch (token.type)
		{
			case PER:
				nextToken();
				LexNameToken name = readNameToken("Expecting name after 'per'");
				checkFor(Token.IMPLIES, 2116, "Expecting  => ");
				ASTExpression exp = getExpressionReader().readPerExpression();
				return new ASTPerSyncDefinition(token.location, name, exp);

			case MUTEX:
				nextToken();
				checkFor(Token.BRA, 2117, "Expecting '(' after mutex");
				LexNameList opnames = new LexNameList();

				switch (lastToken().type)
				{
					case ALL:
						nextToken();
						checkFor(Token.KET, 2118, "Expecting ')' after 'all'");
						break;

					default:
						LexNameToken op = readNameToken("Expecting a name");
						opnames.add(op);

						while (ignore(Token.COMMA))
						{
							op = readNameToken("Expecting a name");
							opnames.add(op);
						}

						checkFor(Token.KET, 2119, "Expecting ')'");
						break;
				}

				return new ASTMutexSyncDefinition(token.location, opnames);

			default:
				throwMessage(2028, "Expecting 'per' or 'mutex'");
				return null;
		}
	}

	private ASTDefinition readNamedTraceDefinition()
		throws ParserException, LexException
	{
		LexLocation start = lastToken().location;
		List names = readTraceIdentifierList();
		checkFor(Token.COLON, 2264, "Expecting ':' after trace name(s)");
		ASTTraceDefinitionTermList traces = readTraceDefinitionList();

		return new ASTNamedTraceDefinition(start, names, traces);
	}

	private List readTraceIdentifierList()
		throws ParserException, LexException
	{
		List names = new Vector();
		names.add(readIdToken("Expecting trace identifier").name);

		while (lastToken().is(Token.DIVIDE))
		{
			nextToken();
			names.add(readIdToken("Expecting trace identifier").name);
		}

		return names;
	}

	private ASTTraceDefinitionTermList readTraceDefinitionList()
		throws LexException, ParserException
	{
		ASTTraceDefinitionTermList list = new ASTTraceDefinitionTermList();
		list.add(readTraceDefinitionTerm());

		while (lastToken().is(Token.SEMICOLON))
		{
			try
			{
				reader.push();
				nextToken();
				list.add(readTraceDefinitionTerm());
				reader.unpush();
			}
	    	catch (ParserException e)
	    	{
	    		reader.pop();
				break;
	    	}
		}

		return list;
	}

	private ASTTraceDefinitionTerm readTraceDefinitionTerm()
		throws LexException, ParserException
	{
		ASTTraceDefinitionTerm term = new ASTTraceDefinitionTerm();
		term.add(readTraceDefinition());

		while (lastToken().is(Token.PIPE))
		{
			nextToken();
			term.add(readTraceDefinition());
		}

		return term;
	}

	private ASTTraceDefinition readTraceDefinition()
		throws LexException, ParserException
	{
		if (lastToken().is(Token.LET))
		{
			return readTraceBinding();
		}
		else
		{
			return readTraceRepeat();
		}
	}

	private ASTTraceDefinition readTraceRepeat()
		throws ParserException, LexException
	{
       	ASTTraceCoreDefinition 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(Token.NUMBER))
				{
					throwMessage(2266, "Expecting '{n}' or '{n1, n2}' after trace definition");
				}

				LexIntegerToken lit = (LexIntegerToken)lastToken();
				from = lit.value.longValue();
				to = lit.value.longValue();

				switch (nextToken().type)
				{
					case COMMA:
						if (nextToken().isNot(Token.NUMBER))
						{
							throwMessage(2265, "Expecting '{n1, n2}' after trace definition");
						}

						lit = (LexIntegerToken)readToken();
						to = lit.value.longValue();
						checkFor(Token.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 new ASTTraceRepeatDefinition(token.location, core, from, to);
	}

	private ASTTraceDefinition readTraceBinding()
		throws ParserException, LexException
	{
		checkFor(Token.LET, 2230, "Expecting 'let'");
		ParserException letDefError = null;

		try
		{
			reader.push();
			ASTTraceDefinition def = readLetDefBinding();
			reader.unpush();
			return def;
		}
		catch (ParserException e)
		{
			e.adjustDepth(reader.getTokensRead());
			reader.pop();
			letDefError = e;
		}

		try
		{
			reader.push();
			ASTTraceDefinition def = readLetBeStBinding();
			reader.unpush();
			return def;
		}
		catch (ParserException e)
		{
			e.adjustDepth(reader.getTokensRead());
			reader.pop();
			throw e.deeperThan(letDefError) ? e : letDefError;
		}
	}

	private ASTTraceDefinition readLetDefBinding()
		throws ParserException, LexException
	{
		ASTDefinitionList localDefs = new ASTDefinitionList();
		LexToken start = lastToken();

		ASTDefinition def = readLocalDefinition();

		if (!(def instanceof ASTValueDefinition))
		{
			throwMessage(2270, "Only value definitions allowed in traces");
		}

		localDefs.add((ASTValueDefinition)def);

		while (ignore(Token.COMMA))
		{
			def = readLocalDefinition();

			if (!(def instanceof ASTValueDefinition))
			{
				throwMessage(2270, "Only value definitions allowed in traces");
			}

			localDefs.add((ASTValueDefinition)def);
		}

		checkFor(Token.IN, 2231, "Expecting 'in' after local definitions");
		ASTTraceDefinition body = readTraceDefinition();

		return new ASTTraceLetDefBinding(start.location, localDefs, body);
	}

	private ASTTraceDefinition readLetBeStBinding()
		throws ParserException, LexException
	{
		LexToken start = lastToken();
		ASTMultipleBind bind = getBindReader().readMultipleBind();
		ASTExpression stexp = null;

		if (lastToken().is(Token.BE))
		{
			nextToken();
			checkFor(Token.ST, 2232, "Expecting 'st' after 'be' in let statement");
			stexp = getExpressionReader().readExpression();
		}

		checkFor(Token.IN, 2233, "Expecting 'in' after bind in let statement");
		ASTTraceDefinition body = readTraceDefinition();

		return new ASTTraceLetBeStBinding(start.location, bind, stexp, body);
	}

	private ASTTraceCoreDefinition readCoreTraceDefinition()
		throws ParserException, LexException
	{
		LexToken token = lastToken();

		switch (token.type)
		{
			case IDENTIFIER:
			case NAME:
			case SELF:
				StatementReader sr = getStatementReader();
				ASTStatement stmt = sr.readCallStatement();

				if (!(stmt instanceof ASTCallStatement) &&
					!(stmt instanceof ASTCallObjectStatement))
				{
					throwMessage(2267,
						"Expecting 'obj.op(args)' or 'op(args)'", token);
				}

				return new ASTTraceApplyExpression(stmt);

			case BRA:
				nextToken();
				ASTTraceDefinitionTermList list = readTraceDefinitionList();
				LexLocation semi = lastToken().location;
				
				if (ignore(Token.SEMICOLON) && Settings.strict)
				{
					warning(5029, "Strict: unexpected trailing semi-colon", semi);
				}
				
				checkFor(Token.KET, 2269, "Expecting '(trace definitions)'");
				return new ASTTraceBracketedExpression(token.location, list);

			case PIPEPIPE:
				nextToken();
				checkFor(Token.BRA, 2292, "Expecting '|| (...)'");
				ASTTraceDefinitionList defs = new ASTTraceDefinitionList();
				defs.add(readTraceDefinition());
				checkFor(Token.COMMA, 2293, "Expecting '|| (a, b {,...})'");
				defs.add(readTraceDefinition());

				while (lastToken().is(Token.COMMA))
				{
					nextToken();
					defs.add(readTraceDefinition());
				}

				checkFor(Token.KET, 2294, "Expecting ')' ending || clause");
				return new ASTTraceConcurrentExpression(token.location, defs);

			default:
				throwMessage(2267, "Expecting 'obj.op(args)' or 'op(args)'", token);
				return null;
		}
	}
	
	private ASTExpression readFunctionBody() throws LexException, ParserException
	{
		LexToken token = lastToken();

		if (token.is(Token.IS))
		{
			switch (nextToken().type)
			{
				case NOT:
					nextToken();
					checkFor(Token.YET, 2125, "Expecting 'is not yet specified'");
					checkFor(Token.SPECIFIED, 2126, "Expecting 'is not yet specified'");
					return new ASTNotYetSpecifiedExpression(token.location);
	
				case SUBCLASS:
					nextToken();
					checkFor(Token.RESPONSIBILITY, 2127, "Expecting 'is subclass responsibility'");
					return new ASTSubclassResponsibilityExpression(token.location);

				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 ASTStatement readOperationBody() throws LexException, ParserException
	{
		LexToken token = lastToken();

		if (token.is(Token.IS))
		{
			switch (nextToken().type)
			{
				case NOT:
					nextToken();
					checkFor(Token.YET, 2187, "Expecting 'is not yet specified");
					checkFor(Token.SPECIFIED, 2188, "Expecting 'is not yet specified");
					return new ASTNotYetSpecifiedStatement(token.location);

				case SUBCLASS:
					nextToken();
					checkFor(Token.RESPONSIBILITY, 2189, "Expecting 'is subclass responsibility'");
					return new ASTSubclassResponsibilityStatement(token.location);

				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();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy