/// <inheritdoc />
		public ProductionRule Read(ITerm representation)
		{
			// CONTRACT: Inherited from IProductionFormat<T>

			IConsTerm prod = representation.ToCons("prod", 3);
			IListTerm rhs = (IListTerm)prod[0];
			ITerm lhs = prod[1];
			ITerm attr = prod[2];

			var reduction = ReadReduction(lhs);
			var expression = ReadExpression(rhs);
			string constructor = ReadConstructor(attr);
			var type = ReadProductionType(attr);
			var flags = ReadProductionFlags(attr);
			// TODO: Error on unknown attributes

			return new ProductionRule(reduction, expression, constructor, type, flags);
		}
		/// <summary>
		/// Parses the parse table from the specified AST.
		/// </summary>
		/// <param name="ast">The AST.</param>
		/// <returns>The resulting <see cref="ParseTable"/>.</returns>
		private ParseTable Parse(ITerm ast)
		{
			#region Contract
			Contract.Requires<ArgumentNullException>(ast != null);
			Contract.Ensures(Contract.Result<ParseTable>() != null);
			#endregion
			var pt = ast.ToCons("parse-table", 5);
			int version = pt[0].ToInt32();
			Assert(version == 4 || version == 6, "Only version 4 or 6 parse tables are supported.");

			var initialState = new StateRef(pt[1].ToInt32());
			var labels = ParseLabels((IListTerm)pt[2]);
			var states = ParseStates((IListTerm)pt[3].ToCons("states", 1)[0], labels);
			var priorities = ParsePriorities((IListTerm)pt[4].ToCons("priorities", 1)[0]);

			// TODO: Injections?

			return new ParseTable(initialState, states, labels, priorities);
		}
		/// <summary>
		/// Reads the production flags from a term.
		/// </summary>
		/// <param name="term">The term.</param>
		/// <returns>The production flags.</returns>
		private ProductionFlags ReadProductionFlags(ITerm term)
		{
			#region Contract
			Contract.Requires<ArgumentNullException>(term != null);
			#endregion

			if (term.IsCons("no-attrs", 0))
				return ProductionFlags.None;

			ProductionFlags flags = ProductionFlags.None;
			IListTerm list = (IListTerm)term.ToCons("attrs", 1)[0];
			foreach (var t in list.SubTerms.OfType<IConsTerm>())
			{
				if (t.SubTerms.Count != 0)
					continue;

				switch (t.Name)
				{
					case "recover": flags |= ProductionFlags.Recover; break;
					case "completion": flags |= ProductionFlags.Completion; break;
					case "ignore-indent":
					case "ignore-layout": flags |= ProductionFlags.IgnoreLayout; break;
					case "enforce-newline": flags |= ProductionFlags.NewlineEnforced; break;
					case "longest-match": flags |= ProductionFlags.LongestMatch; break;
				}
			}

			return flags;
		}
		/// <summary>
		/// Reads the production type from a term.
		/// </summary>
		/// <param name="term">The term.</param>
		/// <returns>The production type.</returns>
		private ProductionType ReadProductionType(ITerm term)
		{
			#region Contract
			Contract.Requires<ArgumentNullException>(term != null);
			Contract.Ensures(Enum.IsDefined(typeof(ProductionType), Contract.Result<ProductionType>()));
			#endregion

			if (term.IsCons("no-attrs", 0))
				return ProductionType.None;

			ProductionType type = ProductionType.None;
			IListTerm list = (IListTerm)term.ToCons("attrs", 1)[0];
			foreach (var t in list.SubTerms.OfType<IConsTerm>())
			{
				if (t.SubTerms.Count != 0)
					continue;

				// TODO: Error when the type is set more than once.

				switch (t.Name)
				{
					case "reject": type = ProductionType.Reject; break;
					case "prefer": type = ProductionType.Prefer; break;
					case "avoid": type = ProductionType.Avoid; break;
					case "bracket": type = ProductionType.Bracket; break;
					case "assoc":
						{
							var a = t[0];
							if (a.IsCons("left", 0) || a.IsCons("assoc", 0))
								type = ProductionType.LeftAssociative;
							else if (a.IsCons("right", 0))
								type = ProductionType.RightAssociative;
							else if (a.IsCons("non-assoc", 0))
								type = ProductionType.None;
							else
								throw new InvalidOperationException("Unknown associativity: " + a);
						}
						break;
				}
			}

			return type;
		}
		/// <summary>
		/// Reads the constructor name from a term.
		/// </summary>
		/// <param name="term">The term.</param>
		/// <returns>The constructor name.</returns>
		private string ReadConstructor(ITerm term)
		{
			#region Contract
			Contract.Requires<ArgumentNullException>(term != null);
			#endregion

			if (term.IsCons("no-attrs", 0))
				return null;

			IListTerm list = (IListTerm)term.ToCons("attrs", 1)[0];
			var consTerms = list.SubTerms.Select(t => t.AsCons("cons", 1)).Where(c => c != null);
			IConsTerm consTerm = consTerms.SingleOrDefault();
			return consTerm?[0]?.ToString();
		}