/*========================================================================
Copyright (C) 2006 by Geir-Tore Lindsve, Torbjørn Meistad
and Yngve Raudberget, hereby refered to as "the authors".
All rights reserved
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, reproduce, prepare derivative
works, distribute, and display this software and its documentation
for NONCOMMERCIAL RESEARCH AND EDUCATIONAL PURPOSES, provided
that (1) the above copyright notice and the following two paragraphs
appear in all copies of the source code and (2) redistributions,
including without limitation binaries, reproduce these notices in
the supporting documentation.
IN NO EVENT SHALL THE AUTHORS, OR DISTRIBUTORS OF THIS SOFTWARE
BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
AND ITS DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE
PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE AUTHORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO
OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
========================================================================*/
using System;
using System.Collections.Generic;
using System.Text;
using Iesi.Collections;
using System.Collections;
using CLab.Exceptions;
namespace CLab.Data
{
///
/// Class for checking if the problem is valid. Runs type and variable checks.
///
public class Symbols
{
private CP cp;
//Type variables
private Set allTypes;
private Set enumerationTypes;
private Hashtable enumerationItems;
private Set allEnumerationItems;
private Set rangeTypes;
//Variable variables
private Set allVariables;
private Set enumerationVariables;
private Hashtable enumerationVariableType;
private Set rangeVariables;
private Set boolVariables;
//Other
private StringComparer stringComparer;
///
/// Initializes a new instance of the class.
///
/// The CP object.
public Symbols(CP cp)
{
stringComparer = StringComparer.Create(System.Globalization.CultureInfo.CurrentCulture, true);
this.cp = cp;
//Type variables
allTypes = new HybridSet();
enumerationTypes = new HybridSet();
enumerationItems = new Hashtable(stringComparer);
allEnumerationItems = new HybridSet();
rangeTypes = new HybridSet();
//Variable variables
allVariables = new HybridSet();
enumerationVariables = new HybridSet();
enumerationVariableType = new Hashtable(stringComparer);
rangeVariables = new HybridSet();
boolVariables = new HybridSet();
FillAndCheckType();
FillAndCheckVar();
}
///
/// Fills the type list after the type has been checked.
///
public void FillAndCheckType()
{
for (int i = 0; i < cp.Types.Count; i++)
{
if (allTypes.Contains(cp.Types[i].TypeName.ToLower()))
{
throw new ClabSymbolException(String.Format("Duplicate type definition: {0}.", ((Type)cp.Types[i]).TypeName));
}
else allTypes.Add(cp.Types[i].TypeName.ToLower());
switch (cp.Types[i].GetType().ToString())
{
case "CLab.Data.TypeEnum":
{
TypeEnum tvl = (TypeEnum)cp.Types[i];
if (tvl.EnumeratorsList.Count < 2)
{
throw new ClabSymbolException(String.Format("Enumerator list does not contain at least two items: {0}.", cp.Types[i].TypeName));
}
else enumerationTypes.Add(cp.Types[i].TypeName.ToLower());
enumerationTypes.Add(cp.Types[i].TypeName.ToLower());
Set valueSet = new HybridSet();
foreach (String value in tvl.EnumeratorsList)
{
if (allTypes.Contains(value.ToLower()))
{
throw new ClabSymbolException(String.Format("{0} is already defined in {1}.", value, cp.Types[i].TypeName));
}
else valueSet.Add(value.ToLower());
}
allEnumerationItems = (Set) allEnumerationItems.Union(valueSet);
enumerationItems[cp.Types[i].TypeName] = MakeSetOfList(((TypeEnum)cp.Types[i]).EnumeratorsList);
}
break;
case "CLab.Data.TypeRange":
{
TypeRange tr = (TypeRange)cp.Types[i];
if (tr.EndOfRange - tr.StartOfRange + 1 < 2)
{
throw new ClabSymbolException(String.Format("Range ({0}) is too small ({1}).", cp.Types[i].TypeName, (tr.EndOfRange - tr.StartOfRange)));
}
else rangeTypes.Add(cp.Types[i].TypeName.ToLower());
}
break;
default:
throw new ClabInternalErrorException("Symbols.cs: FillAndCheckType() : switch case not covered");
}
}
}
///
/// Fills the variable list after the variable has been checked.
///
public void FillAndCheckVar()
{
for (int i = 0; i < cp.Variables.Count; i++)
{
Variable var = cp.Variables[i];
if (allVariables.Contains(var.VariableName.ToLower()))
{
throw new ClabSymbolException(String.Format("Variable already declared ({0}).", var.VariableName));
}
if (allEnumerationItems.Contains(var.VariableName.ToLower()))
{
throw new ClabSymbolException(String.Format("Variable name already exists ({0})", var.VariableName));
}
allVariables.Add(var.VariableName.ToLower());
if (string.Compare(cp.Variables[i].TypeName, "bool", true) == 0)
boolVariables.Add(var.VariableName.ToLower());
else
{
if (!allTypes.Contains(var.TypeName.ToLower()))
{
throw new ClabSymbolException(String.Format("Variable type is neither declared or bool ({0}).", var.TypeName));
}
//Variable of valuelist type
if (enumerationTypes.Contains(var.TypeName.ToLower()))
{
enumerationVariables.Add(var.VariableName.ToLower());
enumerationVariableType[var.VariableName] = var.TypeName;
}
//Variable of range type
else
rangeVariables.Add(var.VariableName.ToLower());
}
}
}
///
/// Checks if the expressions have valid types.
///
/// The expression.
///
public void CheckExpressionType(Expression expression)
{
switch (expression.Type)
{
case Common.ExprType.et_id:
ExpressionId idExpression = (ExpressionId)expression;
if (!allVariables.Contains(idExpression.Id.ToLower()) & !allEnumerationItems.Contains(idExpression.Id.ToLower()))
throw new ClabSymbolException(String.Format("Variable or enumeration constant {0} is not declared", idExpression.Id));
if (enumerationVariables.Contains(idExpression.Id.ToLower()))
throw new ClabSymbolException(String.Format("Illegal enumeration expression for enumeration variable {0}", idExpression.Id));
if (allEnumerationItems.Contains(idExpression.Id.ToLower()))
throw new ClabSymbolException(String.Format("Illegal enumeration expression for enumeration constant {0}", idExpression.Id));
break;
case Common.ExprType.et_eq:
case Common.ExprType.et_ne:
ExpressionBinary binExpression = (ExpressionBinary)expression;
bool enumerationVariablesLeft = false;
bool enumerationVariablesRight = false;
bool allEnumerationItemsLeft = false;
bool allEnumerationItemsRight = false;
String var = "";
String item = "";
//Checking left side
if (binExpression.Left.Type.Equals(Common.ExprType.et_id))
{
ExpressionId leftIdExpression = (ExpressionId)binExpression.Left;
if (!allVariables.Contains(leftIdExpression.Id.ToLower()) & !allEnumerationItems.Contains(leftIdExpression.Id.ToLower()))
throw new ClabSymbolException(String.Format("Left argument {0} of ==/!= expression {1} is not a declared variable or enumeration constant", leftIdExpression.Id, expression));
else if (enumerationVariables.Contains(leftIdExpression.Id.ToLower()))
{
enumerationVariablesLeft = true;
var = leftIdExpression.Id;
}
else if (allEnumerationItems.Contains(leftIdExpression.Id.ToLower()))
{
allEnumerationItemsLeft = true;
item = leftIdExpression.Id;
}
}
//Checking right side
if (binExpression.Right.Type.Equals(Common.ExprType.et_id))
{
ExpressionId rightIdExpression = (ExpressionId)binExpression.Right;
if (!allVariables.Contains(rightIdExpression.Id.ToLower()) & !allEnumerationItems.Contains(rightIdExpression.Id.ToLower()))
{
if (!allVariables.Contains(rightIdExpression.Id.ToLower()))
Console.WriteLine("!allVariables. Count = " + allVariables.Count);
if (!allEnumerationItems.Contains(rightIdExpression.Id.ToLower()))
Console.WriteLine("!allEnumerationItems. Count = " + allEnumerationItems.Count);
throw new ClabSymbolException(String.Format("Right argument {0} of ==/!= expression {1} is not a declared variable or enumeration constant", rightIdExpression.Id, expression));
}
else if (enumerationVariables.Contains(rightIdExpression.Id.ToLower()))
{
enumerationVariablesRight = true;
var = rightIdExpression.Id;
}
else if (allEnumerationItems.Contains(rightIdExpression.Id.ToLower()))
{
allEnumerationItemsRight = true;
item = rightIdExpression.Id;
}
}
//If either an enum variable or const on left or right side we can check without a recursive call
if (enumerationVariablesLeft | enumerationVariablesRight | allEnumerationItemsLeft | allEnumerationItemsRight)
{
if (enumerationVariablesLeft & allEnumerationItemsRight | enumerationVariablesRight & allEnumerationItemsLeft)
{
if (!((Set)enumerationItems[enumerationVariableType[var]]).Contains(item.ToLower()))
throw new ClabSymbolException(String.Format("In some == or != expression: the enumeration constant {0} is not a member of {1}'s enumeration type {2} ={3}", item, var, enumerationVariableType[var], ((TypeEnum)enumerationItems[enumerationVariableType[var]]).GetDomain));
}
else
//Illegal enum expression
{
String error = "Error in some ==/!= expression involving\n";
if (!var.Equals("")) error += "\tEnumeration variable: " + var + "\n";
if (!item.Equals("")) error += "\tEnumeration constant: " + item + "\n";
throw new ClabSymbolException(error);
}
}
else
{
//Not an enum expression - Make recursive check
CheckExpressionType(binExpression.Left);
CheckExpressionType(binExpression.Right);
}
break;
case Common.ExprType.et_val:
//Leaf case always ok
break;
case Common.ExprType.et_neg:
case Common.ExprType.et_not:
ExpressionNotNeg nnExpression = (ExpressionNotNeg)expression;
CheckExpressionType(nnExpression.Left);
break;
case Common.ExprType.et_impl:
case Common.ExprType.et_or:
case Common.ExprType.et_and:
case Common.ExprType.et_lte:
case Common.ExprType.et_gte:
case Common.ExprType.et_lt:
case Common.ExprType.et_gt:
case Common.ExprType.et_minus:
case Common.ExprType.et_plus:
case Common.ExprType.et_mod:
case Common.ExprType.et_div:
case Common.ExprType.et_times:
binExpression = (ExpressionBinary)expression;
//Not an enum expression - Make recursive check
CheckExpressionType(binExpression.Left);
CheckExpressionType(binExpression.Right);
break;
default:
throw new ClabInternalErrorException("Symbols.cs: CheckExpressionType : switch case not covered");
}
}
///
/// Makes a set of a list.
///
/// The original list.
/// The created set
public Set MakeSetOfList(List list)
{
Set res = new HybridSet();
foreach (String s in list)
{
res.Add(s.ToLower());
}
return res;
}
///
/// Gets the enumeration variables.
///
public Set EnumerationVariables
{
get { return this.enumerationVariables; }
}
///
/// Gets the set of all variables.
///
public Set AllVariables
{
get { return allVariables; }
}
///
/// Gets or sets the range variables.
///
/// The range variables.
public Set RangeVariables
{
get { return rangeVariables; }
set { rangeVariables = value; }
}
///
/// Gets or sets the bool variables.
///
/// The bool variables.
public Set BoolVariables
{
get { return boolVariables; }
set { boolVariables = value; }
}
}
}