// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// This software may be modified and distributed under the terms
// of the BSD license. See the LICENSE file for details.
package wyc.lang;
import java.io.*;
import java.util.*;
import wybs.lang.Attribute;
import wybs.lang.CompilationUnit;
import wybs.lang.SyntacticElement;
import wybs.lang.SyntaxError;
import wybs.util.AbstractCompilationUnit;
import wyc.io.WhileyFileLexer;
import wyc.io.WhileyFileParser;
import wyfs.lang.Content;
import wyfs.lang.Path;
import wyfs.util.Trie;
import wyil.lang.*;
/**
* Provides classes representing the various kinds of declaration found in a
* Whiley source file. This includes <i>type declarations</i>, <i>method
* declarations</i>, <i>constant declarations</i>, etc. In essence, a
* <code>WhileyFile</code> forms the root of the Abstract Syntax Tree.
*
* @author David J. Pearce
*
*/
public final class WhileyFile extends AbstractCompilationUnit {
// =========================================================================
// Content Type
// =========================================================================
public static final Content.Type<WhileyFile> ContentType = new Content.Type<WhileyFile>() {
public Path.Entry<WhileyFile> accept(Path.Entry<?> e) {
if (e.contentType() == this) {
return (Path.Entry<WhileyFile>) e;
}
return null;
}
/**
* This method simply parses a whiley file into an abstract syntax tree.
* It makes little effort to check whether or not the file is
* syntactically correct. In particular, it does not determine the
* correct type of all declarations, expressions, etc.
*
* @param file
* @return
* @throws IOException
*/
@Override
public WhileyFile read(Path.Entry<WhileyFile> e, InputStream inputstream)
throws IOException {
Runtime runtime = Runtime.getRuntime();
long start = System.currentTimeMillis();
long memory = runtime.freeMemory();
WhileyFileLexer wlexer = new WhileyFileLexer(e);
WhileyFileParser wfr = new WhileyFileParser(e, wlexer.scan());
return wfr.read();
}
@Override
public void write(OutputStream output, WhileyFile value) {
// for now
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return "Content-Type: whiley";
}
@Override
public String getSuffix() {
return "whiley";
}
};
// =========================================================================
// State
// =========================================================================
public final ArrayList<Declaration> declarations;
// =========================================================================
// Constructors
// =========================================================================
public WhileyFile(Path.Entry<WhileyFile> entry) {
super(entry);
this.declarations = new ArrayList<>();
}
// =========================================================================
// Accessors
// =========================================================================
public boolean hasName(String name) {
return declaration(name) != null;
}
public NamedDeclaration declaration(String name) {
for (Declaration d : declarations) {
if (d instanceof NamedDeclaration) {
NamedDeclaration nd = (NamedDeclaration) d;
if(nd.name().equals(name)) {
return (NamedDeclaration) nd;
}
}
}
return null;
}
public <T> List<T> declarations(Class<T> c) {
ArrayList<T> r = new ArrayList<>();
for (Declaration d : declarations) {
if (c.isInstance(d)) {
r.add((T) d);
}
}
return r;
}
public <T> List<T> declarations(Class<T> c, String name) {
ArrayList<T> r = new ArrayList<>();
for (Declaration d : declarations) {
if (d instanceof NamedDeclaration
&& ((NamedDeclaration) d).name().equals(name)
&& c.isInstance(d)) {
r.add((T) d);
}
}
return r;
}
public Type typeDecl(String name) {
for (Declaration d : declarations) {
if (d instanceof Type && ((NamedDeclaration)d).name().equals(name)) {
return (Type) d;
}
}
return null;
}
// =========================================================================
// Mutators
// =========================================================================
public void add(Declaration declaration) {
declarations.add(declaration);
}
// =========================================================================
// Types
// =========================================================================
public interface Declaration extends SyntacticElement {
}
public abstract class NamedDeclaration extends AbstractContext implements
Declaration {
private final ArrayList<Modifier> modifiers;
private final String name;
public NamedDeclaration(String name, Collection<Modifier> modifiers,Attribute... attributes) {
super(attributes);
this.modifiers = new ArrayList<>(modifiers);
this.name = name;
}
public String name() {
return name;
}
public List<Modifier> modifiers() {
return modifiers;
}
public boolean hasModifier(Modifier modifier) {
return modifiers.contains(modifier);
}
}
public interface Context extends SyntacticElement {
public WhileyFile file();
public List<Import> imports();
}
private abstract class AbstractContext extends SyntacticElement.Impl
implements Context {
private AbstractContext(Attribute... attributes) {
super(attributes);
}
private AbstractContext(Collection<Attribute> attributes) {
super(attributes);
}
@Override
public WhileyFile file() {
return WhileyFile.this;
}
/**
* Construct an appropriate list of import statements for a declaration
* in a given file. Thus, only import statements up to and including the
* given declaration will be included in the returned list.
*
* @param wf
* --- Whiley File in question to obtain list of import
* statements.
* @param decl
* --- declaration in Whiley File for which the list is
* desired.
* @return
*/
@Override
public List<Import> imports() {
// this computation could (should?) be cached.
ArrayList<Import> imports = new ArrayList<>();
imports.add(new WhileyFile.Import(Trie.fromString(entry.id().parent(),
"*"), null));
for (Declaration d : declarations) {
if (d == this) {
break;
} else if (d instanceof Import) {
imports.add((Import) d);
}
}
imports.add(new WhileyFile.Import(Trie.fromString(entry.id()), "*"));
Collections.reverse(imports);
return imports;
}
}
/**
* Represents an import declaration in a Whiley source file, which has the
* form:
*
* <pre>
* ImportDeclaration ::= "import" [Identifier|Star "from"] Identifier ('.' Identifier|'*')*
* </pre>
*
* The following illustrates a simple import statement:
*
* <pre>
* import Console from whiley.lang.System
* </pre>
*
* Here, the package is <code>whiley.lang</code>, the module is
* <code>System</code> and the name is <code>Console</code>.
*
* @author David J. Pearce
*
*/
public static class Import extends SyntacticElement.Impl implements
Declaration {
public final Trie filter;
public final String name;
public Import(Trie filter, String name, Attribute... attributes) {
super(attributes);
this.filter = filter;
this.name = name;
}
}
/**
* Represents a constant declaration in a Whiley source file, which has the
* form:
*
* <pre>
* ConstantDeclaration ::= "constant" Identifier "is" Expression
* </pre>
*
* A simple example to illustrate is:
*
* <pre>
* constant PI is 3.141592654
* </pre>
*
* Here, we are defining a constant called <code>PI</code> which represents
* the decimal value "3.141592654". Constant declarations may also have
* modifiers, such as <code>public</code> and <code>private</code>.
*
* @author David J. Pearce
*
*/
public class Constant extends NamedDeclaration {
public Expr constant;
public wyil.lang.Constant resolvedValue;
public Constant(List<Modifier> modifiers, Expr constant, String name,
Attribute... attributes) {
super(name, modifiers, attributes);
this.constant = constant;
}
}
/**
* Represents a type declaration in a Whiley source file, which has the
* form:
*
* <pre>
* "type" Identifier "is" TypePattern ["where" Expression]
* </pre>
*
* Here, the type pattern specifies a type which may additionally be adorned
* with variable names. The "where" clause is optional and is often referred
* to as the type's "constraint". Variables defined within the type pattern
* may be used within this constraint expressions. A simple example to
* illustrate is:
*
* <pre>
* type nat is (int x) where x >= 0
* </pre>
*
* Here, we are defining a <i>constrained type</i> called <code>nat</code>
* which represents the set of natural numbers (i.e the non-negative
* integers). Type declarations may also have modifiers, such as
* <code>public</code> and <code>private</code>.
*
* @author David J. Pearce
*
*/
public class Type extends NamedDeclaration {
public final Parameter parameter;
public wyil.lang.Type resolvedType;
public ArrayList<Expr> invariant;
public Type(List<Modifier> modifiers, Parameter type,
String name, List<Expr> constraint, Attribute... attributes) {
super(name, modifiers,attributes);
this.parameter = type;
this.invariant = new ArrayList<>(constraint);
}
}
/**
* Represents a <i>function declaration</i> or <i>method declaration</i> in
* a Whiley source file which have the form:
*
* <pre>
* FunctionDeclaration ::= "function" TypePattern "=>" TypePattern (FunctionMethodClause)* ':' NewLine Block
*
* MethodDeclaration ::= "method" TypePattern "=>" TypePattern (FunctionMethodClause)* ':' NewLine Block
*
* FunctionMethodClause ::= "throws" Type | "requires" Expression | "ensures" Expression
* </pre>
*
* Here, the first type pattern (i.e. before "=>") is referred to as the
* "parameter", whilst the second is referred to as the "return". There are
* three kinds of option clause:
*
* <ul>
* <li><b>Throws clause</b>. This defines the exceptions which may be thrown
* by this function. Multiple clauses may be given, and these are taken
* together as a union. Furthermore, the convention is to specify the throws
* clause before the others.</li>
* <li><b>Requires clause</b>. This defines a constraint on the permissible
* values of the parameters on entry to the function or method, and is often
* referred to as the "precondition". This expression may refer to any
* variables declared within the parameter type pattern. Multiple clauses
* may be given, and these are taken together as a conjunction. Furthermore,
* the convention is to specify the requires clause(s) before any ensure(s)
* clauses.</li>
* <li><b>Ensures clause</b>. This defines a constraint on the permissible
* values of the the function or method's return value, and is often
* referred to as the "postcondition". This expression may refer to any
* variables declared within either the parameter or return type pattern.
* Multiple clauses may be given, and these are taken together as a
* conjunction. Furthermore, the convention is to specify the requires
* clause(s) after the others.</li>
* </ul>
*
* <p>The following function declaration provides a small example to
* illustrate:</p>
*
* <pre>
* function max(int x, int y) -> (int z)
* // return must be greater than either parameter
* ensures x <= z && y <= z
* // return must equal one of the parmaeters
* ensures x == z || y == z:
* ...
* </pre>
*
* <p>Here, we see the specification for the well-known <code>max()</code>
* function which returns the largest of its parameters. This does not throw
* any exceptions, and does not enforce any preconditions on its parameters.</p>
*
* <p>
* Function and method declarations may also have modifiers, such as
* <code>public</code> and <code>private</code>.
* </p>
*/
public abstract class FunctionOrMethodOrProperty extends NamedDeclaration {
public final ArrayList<Parameter> parameters;
public final ArrayList<String> lifetimeParameters;
public final ArrayList<Parameter> returns;
public final ArrayList<Stmt> statements;
public List<Expr> requires;
public List<Expr> ensures;
/**
* Construct an object representing a Whiley function.
*
* @param name
* - The name of the function.
* @param returnType
* - The return type of this method
* @param paramTypes
* - The list of parameter names and their types for this
* method
* @param requires
* - The constraints which must hold true on entry
* @param ensures
* - The constraints which must hold true on exit
* @param statements
* - The Statements making up the function body.
*/
public FunctionOrMethodOrProperty(List<Modifier> modifiers, String name,
List<Parameter> returns, List<Parameter> parameters,
List<String> lifetimeParameters,
List<Expr> requires, List<Expr> ensures,
List<Stmt> statements,
Attribute... attributes) {
super(name, modifiers,attributes);
this.returns = new ArrayList<>(returns);
this.parameters = new ArrayList<>(parameters);
this.lifetimeParameters = lifetimeParameters == null ? new ArrayList<>() : new ArrayList<>(lifetimeParameters);
this.requires = new ArrayList<>(requires);
this.ensures = new ArrayList<>(ensures);
this.statements = new ArrayList<>(statements);
}
public abstract SyntacticType.FunctionOrMethod unresolvedType();
public abstract wyil.lang.Type.FunctionOrMethod resolvedType();
}
/**
* Represents a function declaration in a Whiley source file. For example:
*
* <pre>
* function f(int x) -> (int y)
* // Parameter must be positive
* requires x > 0
* // Return must be negative
* ensures y < 0:
* // body
* return -x
* </pre>
*
* <p>
* Here, a function <code>f</code> is defined which accepts only positive
* integers and returns only negative integers. The special variable
* <code>$</code> is used to refer to the return value. Functions in Whiley
* may not have side-effects (i.e. they are <code>pure functions</code>).
* </p>
*
* <p>
* Function declarations may also have modifiers, such as
* <code>public</code> and <code>private</code>.
* </p>
*
* <p>
* <b>NOTE</b> see {@link FunctionOrMethodOrProperty} for more information.
* </p>
*
* @see FunctionOrMethodOrProperty
*
* @author David J. Pearce
*
*/
public final class Function extends FunctionOrMethodOrProperty {
public wyil.lang.Type.Function resolvedType;
public Function(List<Modifier> modifiers, String name, List<Parameter> returns,
List<Parameter> parameters, List<Expr> requires,
List<Expr> ensures,
List<Stmt> statements, Attribute... attributes) {
super(modifiers, name, returns, parameters, null, requires, ensures, statements, attributes);
}
@Override
public SyntacticType.Function unresolvedType() {
ArrayList<SyntacticType> paramTypes = new ArrayList<>();
for (Parameter p : parameters) {
paramTypes.add(p.type);
}
ArrayList<SyntacticType> returnTypes = new ArrayList<>();
for (Parameter r : returns) {
returnTypes.add(r.type);
}
return new SyntacticType.Function(returnTypes, paramTypes, attributes());
}
@Override
public wyil.lang.Type.Function resolvedType() {
return resolvedType;
}
}
/**
* Represents a method declaration in a Whiley source file. For example:
*
* <pre>
* method m(int x) -> (int y)
* // Parameter must be positive
* requires x > 0
* // Return must be negative
* ensures $ < 0:
* // body
* return -x
* </pre>
*
* <p>
* Here, a method <code>m</code> is defined which accepts only positive
* integers and returns only negative integers. The special variable
* <code>$</code> is used to refer to the return value. Unlike functions,
* methods in Whiley may have side-effects.
* </p>
*
* <p>
* Method declarations may also have modifiers, such as <code>public</code>
* and <code>private</code>.
* </p>
*
* <p>
* <b>NOTE</b> see {@link FunctionOrMethodOrProperty} for more information.
* </p>
*
* @author David J. Pearce
*
*/
public final class Method extends FunctionOrMethodOrProperty {
public wyil.lang.Type.Method resolvedType;
public Method(List<Modifier> modifiers, String name, List<Parameter> returns, List<Parameter> parameters,
List<String> lifetimeParameters, List<Expr> requires, List<Expr> ensures,
List<Stmt> statements, Attribute... attributes) {
super(modifiers, name, returns, parameters, lifetimeParameters, requires, ensures, statements, attributes);
}
@Override
public SyntacticType.Method unresolvedType() {
ArrayList<SyntacticType> parameterTypes = new ArrayList<>();
for (Parameter p : parameters) {
parameterTypes.add(p.type);
}
ArrayList<SyntacticType> returnTypes = new ArrayList<>();
for (Parameter r : returns) {
returnTypes.add(r.type);
}
return new SyntacticType.Method(returnTypes, parameterTypes,
Collections.<String>emptySet(), lifetimeParameters, attributes());
}
@Override
public wyil.lang.Type.Method resolvedType() {
return resolvedType;
}
}
/**
* Represents a function declaration in a Whiley source file. For example:
*
* <pre>
* function f(int x) -> (int y)
* // Parameter must be positive
* requires x > 0
* // Return must be negative
* ensures y < 0:
* // body
* return -x
* </pre>
*
* <p>
* Here, a function <code>f</code> is defined which accepts only positive
* integers and returns only negative integers. The special variable
* <code>$</code> is used to refer to the return value. Functions in Whiley
* may not have side-effects (i.e. they are <code>pure functions</code>).
* </p>
*
* <p>
* Function declarations may also have modifiers, such as
* <code>public</code> and <code>private</code>.
* </p>
*
* <p>
* <b>NOTE</b> see {@link FunctionOrMethodOrProperty} for more information.
* </p>
*
* @see FunctionOrMethodOrProperty
*
* @author David J. Pearce
*
*/
public final class Property extends FunctionOrMethodOrProperty {
public wyil.lang.Type.Property resolvedType;
public Property(List<Modifier> modifiers, String name, List<Parameter> parameters, List<Expr> invariant,
Attribute... attributes) {
super(modifiers, name, Collections.EMPTY_LIST, parameters, null, invariant, Collections.EMPTY_LIST,
Collections.EMPTY_LIST, attributes);
}
@Override
public SyntacticType.Property unresolvedType() {
ArrayList<SyntacticType> paramTypes = new ArrayList<>();
for (Parameter p : parameters) {
paramTypes.add(p.type);
}
return new SyntacticType.Property(paramTypes, attributes());
}
@Override
public wyil.lang.Type.Property resolvedType() {
return resolvedType;
}
}
/**
* Represents a parameter declaration as part of a function or method
* declaration. The primary purpose of this is to retain the source-code
* location of the parameter in case any syntax error needs to be reported
* on it.
*
* @author David J. Pearce
*
*/
public final class Parameter extends AbstractContext {
public final SyntacticType type;
public final String name;
public Parameter(SyntacticType type, String name,
Attribute... attributes) {
super(attributes);
this.type = type;
this.name = name;
}
public Parameter(SyntacticType type, String name,
Collection<Attribute> attributes) {
super(attributes);
this.type = type;
this.name = name;
}
public String name() {
return name;
}
}
public static void syntaxError(String msg, Context context,
SyntacticElement elem) {
throw new SyntaxError(msg, context.file().getEntry(), elem);
}
public static void syntaxError(String msg, Context context,
SyntacticElement elem, Throwable ex) {
throw new SyntaxError(msg, context.file().getEntry(), elem, ex);
}
public static void internalFailure(String msg, Context context,
SyntacticElement elem) {
throw new SyntaxError.InternalFailure(msg, context.file().getEntry(), elem);
}
public static void internalFailure(String msg, Context context,
SyntacticElement elem, Throwable ex) {
throw new SyntaxError.InternalFailure(msg, context.file().getEntry(), elem, ex);
}
}