 |
Exercise 6: Solution
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Plato.Core;
namespace Plato.Language.Parser { /// <summary> /// Parses transformations. /// </summary> public class TransformationParser { Brain brain;
public TransformationParser(Brain brain) { this.brain = brain; }
/// <summary> /// Parse the lines of input into transformations. /// </summary> /// <param name="str">The text input.</param> /// <returns>A list of parsed transformations.</returns> public IList<Transformation> Parse(string str) { IList<Transformation> transformations = new List<Transformation>();
string[] lines = str.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
// Process each line of input int lineNum = 0; foreach (string line in lines) { ++lineNum; line.Trim();
if (String.IsNullOrEmpty(line)) { continue; }
try { string input; string output;
if (line.Contains("->")) { string[] parts = line.Split(new string[] { "->" }, StringSplitOptions.None); if (parts.Length != 2) { throw new TooManyArrowsException( "Expecting a single '->'"); } else { input = parts[0].Trim(); output = parts[1].Trim();
TransInput transInput = ParseTransInput(input); ITransOutput transOutput = ParseTransOutput(output);
Transformation t = new Transformation(transInput, transOutput); transformations.Add(t); } } else { throw new MissingArrowException("Missing '->'"); } } catch (AlterableException e) { e.AlterMessage( String.Format("Line {0}: {1}", lineNum, e.Message)); throw e; } catch (Exception e) { throw new Exception( String.Format("Line {0}: {1}", lineNum, e.Message)); } }
return transformations; }
/// <summary> /// Parse a transformation input specification. /// </summary> /// <param name="str">The text input.</param> /// <returns>A parsed transformation input spec.</returns> protected TransInput ParseTransInput(string str) { IList<IInputToken> tokenList = new List<IInputToken>();
string[] tokens = str.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length == 0) { throw new EmptyInputException("The input specification is empty."); }
// Process each token foreach (string token in tokens) { // An entity type token? if (token.StartsWith("{")) { tokenList.Add(ParseEntityTypeToken(token)); } else { // A literal token tokenList.Add(new LiteralToken(token)); } }
return new TransInput(tokenList); }
/// <summary> /// Parse a transformation output specification. /// </summary> /// <param name="str">The text input.</param> /// <returns>A parsed transformation output spec.</returns> protected ITransOutput ParseTransOutput(string str) { if (str.Contains("=")) { return ParseTransOutput_Assignment(str); } else { return ParseTransOutput_Value(str); } }
/// <summary> /// Parse a transformation output specification that is /// an assignment. /// </summary> /// <param name="str">The text input.</param> /// <returns>A parsed transformation output spec.</returns> protected TransOutput_Assignment ParseTransOutput_Assignment(string str) { PropertyToken property; VariableToken value;
string[] parts = str.Split(new string[] { "=" }, StringSplitOptions.None); if (parts.Length != 2) { throw new TooManyEqualSignsException( "Expecting a single '=' in the output specification."); } else { string propertyToken = parts[0].Trim(); string valueToken = parts[1].Trim();
property = ParseProperty(propertyToken); value = ParseVariableToken(valueToken); }
return new TransOutput_Assignment(property, value); }
/// <summary> /// Parse a transformation output specification that is /// a value. /// </summary> /// <param name="str">The text input.</param> /// <returns>A parsed transformation output spec.</returns> protected virtual TransOutput_Value ParseTransOutput_Value(string str) { return new TransOutput_Value(ParseProperty(str)); }
/// <summary> /// Parse an entity type token. /// </summary> /// <param name="token">The text input.</param> /// <returns>The parsed token.</returns> protected EntityTypeToken ParseEntityTypeToken(string token) { if (!token.EndsWith("}")) { throw new MissingClosingBraceException( String.Format("The entity type '{0}' is missing " + "its closing brace.", token)); }
// Remove the curly braces string entityId = token.Substring(1, token.Length - 2);
IEntity entity = brain.Get(entityId);
if (entity == null) { throw new EntityNotFoundException( String.Format("The entity type '{0}' could " + "not be found.", entityId)); }
return new EntityTypeToken(entity); }
/// <summary> /// Parse a variable token. /// </summary> /// <param name="str">The text input.</param> /// <returns>The parsed token.</returns> protected VariableToken ParseVariableToken(string token) { // Variable token try { string variableNumber = token.Substring(1, token.Length - 1); return new VariableToken(Convert.ToUInt32(variableNumber)); } catch { throw new InvalidVariableTokenException( String.Format("The variable token '{0}' is invalid.", token)); } }
/// <summary> /// Parse a property. /// </summary> /// <param name="str">The text input.</param> /// <returns>A parsed property.</returns> protected virtual PropertyToken ParseProperty(string str) { IList<IPropertyPathToken> path = new List<IPropertyPathToken>();
// Note: It is conceivable that a fragment could contain // a period and so this isn't quite right. string[] tokens = str.Split('.');
foreach (string token in tokens) { if (token.StartsWith("((")) { // Fragment token if (token.EndsWith("))")) { string fragmentToken = token.Substring(2, token.Length - 4); FragmentToken fragment = new FragmentToken( ParseOutputFragment(fragmentToken)); path.Add(fragment); } else { throw new InvalidTokenException( String.Format("Invalid: {0}.", token)); } } else if (token.StartsWith("$")) { path.Add(ParseVariableToken(token)); } else if (token.Length == 0) { throw new InvalidPropertyException( String.Format("Invalid property: {0}", str)); } else { // Entity token IEntity entity = brain.Get(token); if (entity == null) { throw new EntityNotFoundException( String.Format("The entity '{0}' could " + "not be found.", token)); } path.Add(new EntityToken(entity)); } }
return new PropertyToken(path); }
/// <summary> /// Parse a fragment. /// </summary> /// <param name="str">The text input.</param> /// <returns>The parsed fragment.</returns> protected Fragment ParseOutputFragment(string str) { IList<IToken> tokenList = new List<IToken>();
string[] tokens = str.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length == 0) { throw new EmptyFragmentException("The fragment is empty."); }
// Process each token foreach (string token in tokens) { if (token.StartsWith("$")) { // Variable token tokenList.Add(ParseVariableToken(token)); } else { // Literal token tokenList.Add(new LiteralToken(token)); } }
return new Fragment(tokenList); } }
/// <summary> /// An exception whos message can be altered. /// </summary> public class AlterableException : Exception { private string message;
public AlterableException(string message) : base(message) { }
public override string Message { get { if (String.IsNullOrEmpty(message)) { // If the message hasn't been altered, return // the message of the original exception. return base.Message; } else { // If the message has been altered, return // the altered message; return message; } } }
public void AlterMessage(string message) { this.message = message; } }
public class MissingArrowException : AlterableException { public MissingArrowException(string message) : base(message) { } }
public class TooManyArrowsException : AlterableException { public TooManyArrowsException(string message) : base(message) { } }
public class EmptyInputException : AlterableException { public EmptyInputException(string message) : base(message) { } }
public class MissingClosingBraceException : AlterableException { public MissingClosingBraceException(string message) : base(message) { } }
public class EntityNotFoundException : AlterableException { public EntityNotFoundException(string message) : base(message) { } }
public class TooManyEqualSignsException : AlterableException { public TooManyEqualSignsException(string message) : base(message) { } }
public class InvalidTokenException : AlterableException { public InvalidTokenException(string message) : base(message) { } }
public class EmptyFragmentException : AlterableException { public EmptyFragmentException(string message) : base(message) { } }
...
|
|
|
|
 |