using System;
using System.Collections.Generic;
namespace DtxCS.DataTypes
{
///
/// Represents an array of DataNodes.
///
public class DataArray : DataNode
{
public virtual char ClosingChar => ')';
///
/// Returns DataType.ARRAY
///
public override DataType Type => DataType.ARRAY;
///
/// The children of this array.
///
public List Children { get; }
///
/// Default constructor for a Data Array.
///
public DataArray()
{
this.Children = new List();
}
///
/// Add a node to this Array.
///
/// Node to add
/// The added node.
public T AddNode(T node) where T : DataNode
{
Children.Add(node);
node.Parent = this;
return node;
}
///
/// Get or set the child of this array at the given index.
///
///
///
public DataNode this[int index]
{
get { return Children[index]; }
set { Children[index] = value; }
}
///
/// Find the array in this array's children at the given index.
///
///
///
public DataArray Array(int idx)
{
if (Children[idx].Type == DataType.ARRAY)
{
return (DataArray)Children[idx];
}
throw new Exception("Element at index " + idx + " is not an Array. It is "+Children[idx].GetType().Name);
}
///
/// Find the integer in this array's children at the given index.
///
///
///
public int Int(int idx)
{
if (Children[idx].Type == DataType.INT)
{
return ((DataAtom)Children[idx]).Int;
}
throw new Exception("Element at index " + idx + " is not an integer.");
}
///
/// Find the float in this array's children at the given index.
///
///
///
public float Float(int idx)
{
if (Children[idx].Type == DataType.FLOAT)
{
return ((DataAtom)Children[idx]).Float;
}
throw new Exception("Element at index " + idx + " is not a float.");
}
public float Number(int idx)
{
if (Children[idx].Type == DataType.FLOAT)
{
return ((DataAtom)Children[idx]).Float;
}
else if (Children[idx].Type == DataType.INT)
{
return ((DataAtom)Children[idx]).Int;
}
throw new Exception("Element at index " + idx + " is not a number.");
}
public DataNode Node(int idx)
{
return Children[idx];
}
///
/// Find the string in this array's children at the given index.
///
///
///
public string String(int idx)
{
if (Children[idx].Type == DataType.STRING)
{
return ((DataAtom)Children[idx]).String;
}
throw new Exception("Element at index " + idx + " is not a string.");
}
///
/// Find the symbol in this array's children at the given index.
///
///
///
public DataSymbol Symbol(int idx)
{
if(Children[idx].Type == DataType.SYMBOL)
{
return (DataSymbol)Children[idx];
}
throw new Exception("Element at index " + idx + " is not a symbol.");
}
///
/// Find the variable in this array's children at the given index.
///
///
///
public DataVariable Var(int idx)
{
if (Children[idx].Type == DataType.VARIABLE)
{
return (DataVariable)Children[idx];
}
throw new Exception("Element at index " + idx + " is not a variable.");
}
///
/// Finds any node at the given index and returns it as a string.
///
///
///
public string Any(int idx)
{
return Children[idx].Name;
}
///
/// Find the first array in this array's children whose name matches.
/// If none is found, returns null.
///
///
///
public DataArray Array(string name)
{
for (int i = 0; i < Children.Count; i++)
{
if (!(Children[i] is DataArray)) continue;
if (Children[i].Name == name)
{
return ((DataArray)Children[i]);
}
}
return null;
}
public override DataNode Evaluate()
{
var returnArray = new DataArray();
foreach(var node in Children)
{
returnArray.AddNode(node.Evaluate());
}
return returnArray;
}
///
/// The string representation of the first element of the array, unless
/// that element is another array, which would result in an empty string.
///
public override string Name => Children[0].Type == DataType.ARRAY ?
"" : Children[0].Name;
///
/// The number of elements in this array.
///
public int Count => Children.Count;
///
/// The string representation of this Array, suitable for putting right
/// back into a .dta file.
///
///
public override string ToString() => ToString(0);
public override string ToString(int depth)
{
string ret = new string(' ', depth*3) + "(";
for (int i = 0; i < Children.Count; i++)
{
var n = Children[i];
ret += n is DataArray ? Environment.NewLine + n.ToString(depth + 1) : n.ToString(depth + 1);
if (i + 1 != Children.Count) ret += " ";
}
ret += ")";
return ret;
}
}
public class DataCommand : DataArray
{
public override DataType Type => DataType.COMMAND;
public override char ClosingChar => '}';
public DataCommand() : base()
{
}
public override string ToString() => ToString(0);
public override string ToString(int depth)
{
string ret = new string(' ', depth*3)+"{";
foreach (DataNode n in Children)
{
ret += (n is DataArray ? Environment.NewLine + n.ToString(depth + 1) : n.ToString(depth + 1) + " ");
}
ret += "}";
return ret;
}
///
/// Returns a copy of this datacommand with all children evaulated.
///
public DataCommand EvalAll()
{
var ret = new DataCommand();
for(var i = 0; i < Children.Count; i++)
{
ret.AddNode(Children[i].Evaluate());
}
return ret;
}
public override DataNode Evaluate()
{
Func f;
if (Builtins.Funcs.TryGetValue(Symbol(0), out f))
{
return f.Invoke(this);
}
throw new Exception($"Func '{Any(0)}' is not defined.");
}
}
// e.g., [RED 1 0 0] in a file, then (color RED) -> (color 1 0 0)
// or, [TRUE 1] -> (happy TRUE) -> (happy 1)
public class DataMacroDefinition : DataArray
{
public override DataType Type => DataType.MACRO;
public override char ClosingChar => ']';
public DataMacroDefinition() : base()
{
}
public override string ToString() => ToString(0);
public override DataNode Evaluate() => this;
public override string ToString(int depth)
{
string ret = new string(' ', depth*3) + "[";
foreach (DataNode n in Children)
{
ret += (n is DataArray ? Environment.NewLine + n.ToString(depth + 1) : n.ToString(depth + 1) + " ");
}
ret += "]";
return ret;
}
}
}