com.fujitsu.vdmj.syntax.SyntaxReader 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.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import com.fujitsu.vdmj.Release;
import com.fujitsu.vdmj.Settings;
import com.fujitsu.vdmj.ast.annotations.ASTAnnotation;
import com.fujitsu.vdmj.ast.annotations.ASTAnnotationList;
import com.fujitsu.vdmj.ast.lex.LexCommentList;
import com.fujitsu.vdmj.ast.lex.LexIdentifierToken;
import com.fujitsu.vdmj.ast.lex.LexNameToken;
import com.fujitsu.vdmj.ast.lex.LexToken;
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.Console;
import com.fujitsu.vdmj.messages.ConsoleWriter;
import com.fujitsu.vdmj.messages.LocatedException;
import com.fujitsu.vdmj.messages.VDMError;
import com.fujitsu.vdmj.messages.VDMWarning;
import com.fujitsu.vdmj.util.GetResource;
/**
* The parent class of all syntax readers.
*/
public abstract class SyntaxReader
{
/** The lexical analyser. */
protected final LexTokenReader reader;
/** The dialect of VDM that we're parsing. */
protected final Dialect dialect;
/** A DefinitionReader, if created. */
protected DefinitionReader definitionReader = null;
/** An ExpressionReader, if created. */
protected ExpressionReader expressionReader = null;
/** A PatternReader, if created. */
protected PatternReader patternReader = null;
/** A TypeReader, if created. */
protected TypeReader typeReader = null;
/** A BindReader, if created. */
protected BindReader bindReader = null;
/** A StatementReader, if created. */
protected StatementReader statementReader = null;
/** A ClassReader, if created. */
protected ClassReader classReader = null;
/** The errors raised. */
private List errors = new Vector();
/** The warnings raised. */
private List warnings = new Vector();
/** The sub-readers defined, if any. */
private List readers = new Vector();
/** The maximum number of syntax errors allowed in one Reader. */
private static final int MAX = 100;
/** A list of class names from the vdmj.annotations resource file */
private static List annotationClasses = null;
/**
* Create a reader with the given lexical analyser and VDM++ flag.
*/
protected SyntaxReader(LexTokenReader reader)
{
this.reader = reader;
this.dialect = reader.dialect;
reader.setSyntaxReader(this);
}
/**
* Read the next token from the lexical analyser, and advance by
* one token.
*
* @return The next token.
*/
protected LexToken nextToken() throws LexException
{
return reader.nextToken();
}
/**
* Return the last token read by the lexical analyser without
* advancing. Repeated calls to this method will return the same
* result.
*
* @return The last token again.
*/
protected LexToken lastToken() throws LexException
{
return reader.getLast();
}
/**
* Return the last token read, and also advance by one token. This
* is equivalent to calling {@link #lastToken} followed by
* {@link #nextToken}, but returning the result of lastToken.
*
* @return The last token.
* @throws LexException
*/
protected LexToken readToken() throws LexException
{
LexToken tok = reader.getLast();
reader.nextToken();
return tok;
}
/**
* Set the name of the current module or class. Unqualified symbol names use
* this as their module/class name. See {@link #idToName}.
*
* @param module
*/
public void setCurrentModule(String module)
{
reader.currentModule = module;
}
/**
* @return The current module/class name.
*/
public String getCurrentModule()
{
return reader.currentModule;
}
/**
* Convert an identifier into a name. A name is an identifier that has
* a module name qualifier, so this method uses the current module to
* convert the identifier passed in.
*
* @param id The identifier to convert
* @return The corresponding name.
*/
protected LexNameToken idToName(LexIdentifierToken id)
{
LexNameToken name = new LexNameToken(reader.currentModule, id);
return name;
}
/**
* Return the last token, converted to a {@link LexIdentifierToken}. If
* the last token is not an identifier token, an exception is thrown
* with the message passed in.
*
* @return The last token as a LexIdentifierToken.
* @throws LexException
*/
protected LexIdentifierToken lastIdToken()
throws ParserException, LexException
{
LexToken tok = reader.getLast();
if (tok.type == Token.IDENTIFIER)
{
LexIdentifierToken id = (LexIdentifierToken)tok;
if (id.old)
{
throwMessage(2295, "Can't use old name " + id + " here", tok);
}
return id;
}
throwMessage(2058, "Expecting Identifier");
return null;
}
/**
* Return the last token, converted to a {@link LexNameToken}. If
* the last token is not a name token, or an identifier token that can
* be converted to a name, an exception is thrown with the message
* passed in.
*
* @return The last token as a LexIdentifierToken.
* @throws LexException
* @throws ParserException
*/
protected LexNameToken lastNameToken()
throws LexException, ParserException
{
LexToken tok = reader.getLast();
if (tok instanceof LexNameToken)
{
LexNameToken name = (LexNameToken)tok;
if (name.old)
{
throwMessage(2295, "Can't use old name " + name + " here", tok);
}
return name;
}
else if (tok instanceof LexIdentifierToken)
{
LexIdentifierToken id = (LexIdentifierToken)tok;
if (id.old)
{
throwMessage(2295, "Can't use old name " + id + " here", tok);
}
return new LexNameToken(reader.currentModule, id);
}
throwMessage(2059, "Expecting a name");
return null;
}
/**
* Return the last token as an identifier, and advance by one token. This
* is similar to calling {@link #lastIdToken} followed by nextToken, and
* returning the result of the lastIdToken.
*
* @param message The message to throw if the last token is not an id.
* @return The last token as a LexIdentifierToken.
* @throws LexException
* @throws ParserException
*/
protected LexIdentifierToken readIdToken(String message)
throws LexException, ParserException
{
return readIdToken(message, false);
}
protected LexIdentifierToken readIdToken(String message, boolean reservedOK)
throws LexException, ParserException
{
LexToken tok = reader.getLast();
if (tok.type == Token.IDENTIFIER)
{
nextToken();
LexIdentifierToken id = (LexIdentifierToken)tok;
if (id.old)
{
throwMessage(2295, "Can't use old name " + id + " here", tok);
}
if (!reservedOK && isReserved(id.name))
{
throwMessage(2295, "Name " + id + " contains a reserved prefix", tok);
}
return id;
}
if (tok.type == Token.NAME)
{
message = "Found qualified name " + tok + ". " + message;
}
throwMessage(2060, message);
return null;
}
/**
* Return the last token as a name, and advance by one token. This
* is similar to calling {@link #lastNameToken} followed by nextToken, and
* returning the result of the lastNameToken.
*
* @param message The message to throw if the last token is not a name.
* @return The last token as a LexNameToken.
* @throws LexException
* @throws ParserException
*/
protected LexNameToken readNameToken(String message)
throws LexException, ParserException
{
return readNameToken(message, false);
}
protected LexNameToken readNameToken(String message, boolean reservedOK)
throws LexException, ParserException
{
LexToken tok = reader.getLast();
nextToken();
if (tok instanceof LexNameToken)
{
LexNameToken name = (LexNameToken)tok;
if (name.old)
{
throwMessage(2295, "Can't use old name " + name + " here", tok);
}
if (isReserved(name.name))
{
throwMessage(2295, "Name " + name + " contains a reserved prefix", tok);
}
return name;
}
else if (tok instanceof LexIdentifierToken)
{
LexIdentifierToken id = (LexIdentifierToken)tok;
if (id.old)
{
throwMessage(2295, "Can't use old name " + id + " here", tok);
}
if (!reservedOK && isReserved(id.name))
{
throwMessage(2295, "Name " + id + " contains a reserved prefix", tok);
}
return new LexNameToken(reader.currentModule, id);
}
throwMessage(2061, message);
return null;
}
/**
* Read any annotations from the collected comments, and clear them. Note that we
* don't parse annotations while inside the annotation parser.
*/
private static int readingAnnotations = 0;
protected ASTAnnotationList readAnnotations(LexCommentList comments) throws LexException, ParserException
{
ASTAnnotationList annotations = new ASTAnnotationList();
if (!Settings.annotations || readingAnnotations > 0)
{
return annotations; // ignore nested annotations
}
else
{
readingAnnotations++;
}
for (int i=0; i afterList = Arrays.asList(after);
List uptoList = Arrays.asList(upto);
try
{
Token tok = lastToken().type;
while (!uptoList.contains(tok) && tok != Token.EOF)
{
if (afterList.contains(tok))
{
nextToken();
break;
}
tok = nextToken().type;
}
}
catch (LexException le)
{
report(le.number, le.getMessage(), le.location);
}
}
/**
* Report a warning. Unlike errors, this does no token recovery.
*/
public void warning(int no, String msg, LexLocation location)
{
if (warnings.size() < MAX)
{
VDMWarning vdmwarning = new VDMWarning(no, msg, location);
warnings.add(vdmwarning);
if (warnings.size() == MAX)
{
warnings.add(new VDMWarning(9, "Too many warnings", location));
// throw new InternalException(9, "Too many warnings");
}
}
}
/**
* Report an error. Unlike errors, this does no token recovery.
*/
public void report(int no, String msg, LexLocation location)
{
if (errors.size() < MAX)
{
VDMError vdmerror = new VDMError(no, msg, location);
errors.add(vdmerror);
if (errors.size() == MAX)
{
errors.add(new VDMError(9, "Too many syntax errors", location));
// throw new InternalException(9, "Too many syntax errors");
}
}
}
/**
* @return The error count from all readers that can raise errors.
*/
public int getErrorCount()
{
int size = 0;
for (SyntaxReader rdr: readers)
{
size += rdr.getErrorCount();
}
return size + errors.size();
}
/**
* @return The errors from all readers that can raise errors.
*/
public List getErrors()
{
List list = new Vector();
for (SyntaxReader rdr: readers)
{
list.addAll(rdr.getErrors());
}
list.addAll(errors);
return list;
}
/**
* @return The warning count from all readers that can raise warnings.
*/
public int getWarningCount()
{
int size = 0;
for (SyntaxReader rdr: readers)
{
size += rdr.getWarningCount();
}
return size + warnings.size();
}
/**
* @return The warnings from all readers that can raise warnings.
*/
public List getWarnings()
{
List list = new Vector();
for (SyntaxReader rdr: readers)
{
list.addAll(rdr.getWarnings());
}
list.addAll(warnings);
return list;
}
/**
* Print errors and warnings to the PrintWriter passed.
*/
public void printErrors(ConsoleWriter out)
{
for (VDMError e: getErrors())
{
out.println(e.toString());
}
}
public void printWarnings(ConsoleWriter out)
{
for (VDMWarning w: getWarnings())
{
out.println(w.toString());
}
}
@Override
public String toString()
{
return reader.toString();
}
protected ASTAnnotation loadAnnotation(LexIdentifierToken name)
throws ParserException, LexException
{
String classpath = Properties.annotations_packages;
String[] packages = classpath.split(";|:");
String astName = "AST" + name + "Annotation";
if (annotationClasses == null)
{
try
{
annotationClasses = GetResource.readResource("vdmj.annotations");
}
catch (Exception e)
{
// ignore
}
}
/*
* The original method to load annotations uses the annotation_packages property.
*/
for (String pack: packages)
{
try
{
Class> clazz = Class.forName(pack + "." + astName);
Constructor> ctor = clazz.getConstructor(LexIdentifierToken.class);
return (ASTAnnotation) ctor.newInstance(name);
}
catch (ClassNotFoundException e)
{
// Try the next package
}
catch (Exception e)
{
throwMessage(2334, "Failed to instantiate " + astName);
}
}
/*
* The preferred method of loading uses an "annotations" resource file, allowing
* annotations to be in any package, though the ASTAnnotation rule remains.
*/
for (String annotationClass: annotationClasses)
{
try
{
if (annotationClass.endsWith("." + astName))
{
Class> clazz = Class.forName(annotationClass);
Constructor> ctor = clazz.getConstructor(LexIdentifierToken.class);
return (ASTAnnotation) ctor.newInstance(name);
}
}
catch (Exception e)
{
throwMessage(2334, "Failed to instantiate " + astName);
}
}
throwMessage(2334, "Cannot find " + astName + " on " + classpath);
return null;
}
protected LexCommentList getComments()
{
return reader.getComments(); // Also clears comments
}
protected boolean isReserved(String name)
{
return
name.startsWith("pre_") ||
name.startsWith("post_") ||
name.startsWith("inv_") ||
name.startsWith("init_") ||
name.startsWith("measure_") ||
Settings.release == Release.VDM_10 &&
(
name.startsWith("eq_") ||
name.startsWith("ord_") ||
name.startsWith("min_") ||
name.startsWith("max_")
);
}
}