using System; using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework.Constraints; using NUnit.Framework; using OCamlC.Lex; using OCamlC.Parse; using OCamlC.AST; using System.Collections; namespace OCamlC.Tests { class Tokens { public static Constraint Are(params object[] tokens) { TokensAre cons = new TokensAre(); cons.Expected = new List(tokens); return cons; } public static Constraint Are(IEnumerable tokens) { TokensAre cons = new TokensAre(); cons.Expected = new List(tokens); return cons; } /// /// avoid ambiguity when passing a single expression. /// public static Constraint Are(ExpectedExpressionTokens tokens) { TokensAre cons = new TokensAre(); cons.Expected = new List(); cons.Expected.Add(tokens); return cons; } private class TokensAre : Constraint { public List Expected; private object _firstExp, _firstAct; private Base _failedToken; private string _failurePos; public override bool Matches(object actual) { base.actual = actual; if (actual is AST.Base) return compareLists(Expected, ((AST.Base)actual).ChildNodes, ""); else return compareLists(Expected, (IEnumerable)actual, ""); } private bool compareLists(List expected, IEnumerable tokens, string contextPos) { //Console.WriteLine("Comparing list starting {0} with some tokens", expected[0]); int expPos = 0; int tokenNum = 0; foreach (AST.Base token in tokens) { // record the current position and token. Then if we // fail, they'll be set to the position and token that // failed. ++tokenNum; string currentPos = String.Format("{0}/{1}", contextPos, tokenNum); _failurePos = currentPos; _failedToken = token; if (token is Terminal) { if (!compareTerminal(expected, ref expPos, (Terminal)token)) return false; } else { // not a terminal, must be a composite of some sort IEnumerable subExp = expected[expPos] as IEnumerable; if (subExp == null) { _firstAct = token; _firstExp = expected[expPos]; return false; } if (!compareLists(new List(subExp), token.ChildNodes, currentPos)) return false; } ++expPos; } if (expPos < expected.Count) { _firstAct = ""; _firstExp = expected[expPos]; _failedToken = null; return false; } //Console.WriteLine("End of list"); return true; } private bool compareTerminal(List expected, ref int expPos, Terminal token) { //Console.WriteLine(" Comparing {0} with token {1}", expected[expPos], token); if (expPos >= expected.Count) { _firstExp = ""; _firstAct = token; return false; } if (expected[expPos] is TokenType) { TokenType expType = (TokenType)expected[expPos]; if (token.Type != expType) { _firstExp = expType; _firstAct = token.Type; return false; } switch (expType) { case TokenType.INFIXSYMBOL: case TokenType.INFIX_PLUSMINUS: case TokenType.INFIX_MULDIV: case TokenType.INFIX_CONCAT: case TokenType.INFIX_COMPARE: case TokenType.INFIX_POWER: if (!doCompare(expected[++expPos], ((InfixSymbol)token).Name)) return false; break; case TokenType.OPERATORSYMBOL: if (!doCompare(expected[++expPos], ((OperatorSymbol)token).Name)) return false; break; case TokenType.PREFIXSYMBOL: if (!doCompare(expected[++expPos], ((PrefixSymbol)token).Name)) return false; break; case TokenType.LABEL: if (!doCompare(expected[++expPos], ((Label)token).Name)) return false; break; case TokenType.OPTLABEL: if (!doCompare(expected[++expPos], ((OptLabel)token).Name)) return false; break; case TokenType.ID: if (!doCompare(expected[++expPos], ((Identifier)token).Name)) return false; break; case TokenType.CHAR: if (!doCompare(expected[++expPos], ((CharLiteral)token).Value)) return false; break; case TokenType.STRING: if (!doCompare(expected[++expPos], ((StringLiteral)token).Value)) return false; break; case TokenType.INT: { ++expPos; IntLiteral intTok = (IntLiteral)token; // XXX handle longs in test long expVal = (int)expected[expPos]; string expAsWritten = null; if (expPos + 1 < expected.Count && expected[expPos + 1] is string) { ++expPos; expAsWritten = (string)expected[expPos]; } if (expVal != intTok.Value) { _firstExp = expVal; _firstAct = intTok.Value; return false; } if (expAsWritten != null && expAsWritten != intTok.AsWritten) { _firstExp = expAsWritten; _firstAct = intTok.AsWritten; return false; } break; } case TokenType.FLOAT: { ++expPos; FloatLiteral floatTok = (FloatLiteral)token; double expVal = (double)expected[expPos]; string expAsWritten = null; if (expPos + 1 < expected.Count && expected[expPos + 1] is string) { ++expPos; expAsWritten = (string)expected[expPos]; } if (expVal != floatTok.Value) { _firstExp = expVal; _firstAct = floatTok.Value; return false; } if (expAsWritten != null && expAsWritten != floatTok.AsWritten) { _firstExp = expAsWritten; _firstAct = floatTok.AsWritten; return false; } break; } default: if (StaticData.KeywordTypes.ContainsKey(expType)) { // nothing more to check for keywords } else { throw new NotImplementedException("Token type " + expType.ToString() + " not understood by LexTokenTest.TokensAre constraint"); } break; } } else { _firstExp = "a token in the expected data"; _firstAct = expected[expPos]; return false; } return true; } private bool doCompare(object exp, TValue actual) { TValue expStr = (TValue)exp; if (! expStr.Equals(actual)) { _firstExp = expStr; _firstAct = actual; return false; } return true; } public override void WriteActualValueTo(MessageWriter writer) { writer.WriteActualValue(_firstAct); //if (_failedToken != null) // writer.WriteActualValue(_failedToken); if (actual is AST.Base) { writer.Write(" in " + AstToString.Read((AST.Base)actual)); } else { StringBuilder sb = new StringBuilder(" in "); foreach (AST.Base node in (IEnumerable)actual) sb.AppendFormat("{0};; ", AstToString.Read(node)); writer.Write(sb.ToString()); } } public override void WriteDescriptionTo(NUnit.Framework.MessageWriter writer) { writer.WriteExpectedValue(_firstExp); writer.WriteExpectedValue(" at " + _failurePos); } } public static ExpectedExpressionTokens SubTree(params object[] details) { return new ExpectedExpressionTokens(details); } /// /// wrapper class to avoid ambiguity when the expected token /// stream contains a single expression -- otherwise we don't /// know whether it's an object[] from the parameters or /// actually meant to be a sublist. /// public class ExpectedExpressionTokens : IEnumerable { private readonly IEnumerable theExp; public ExpectedExpressionTokens(IEnumerable exp) { theExp = exp; } #region IEnumerable Members public IEnumerator GetEnumerator() { return theExp.GetEnumerator(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return theExp.GetEnumerator(); } #endregion } } }