Skip to content

Commit

Permalink
Add new functions for operating with lists
Browse files Browse the repository at this point in the history
Add:

1. head
2. tail
3. length
4. index

Also added some line numbers to exceptions to make things easier for
debugging.
  • Loading branch information
mitchpaulus committed Feb 7, 2021
1 parent ea7ca1b commit aa56862
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 7 deletions.
27 changes: 20 additions & 7 deletions src/IdfPlusExpVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public IdfPlusExpVisitor()
new Dictionary<string, Expression>(MathematicalFunction.FunctionDict)
};
_variables[0]["map"] = new MapFunctionExpression();
_variables[0]["load"] = new LoadFunctionExpression();
_variables[0]["head"] = new ListHeadFunctionExpression();
_variables[0]["tail"] = new ListTailFunctionExpression();
_variables[0]["index"] = new ListIndexFunctionExpression();
_variables[0]["length"] = new ListLengthFunctionExpression();
}

private readonly Dictionary<string, Func<double, double, double>> _numericOperatorMapping =
Expand Down Expand Up @@ -54,7 +59,7 @@ public override Expression VisitVariableExp(IdfplusParser.VariableExpContext con
var found = scope.TryGetValue(context.GetText(), out Expression value);
if (found) return value;
}
throw new InvalidOperationException($"Could not find variable {context.GetText()} in scope.");
throw new InvalidOperationException($"Line {context.Start.Line}: Could not find variable {context.GetText()} in scope.");
}

public override Expression VisitLogicExp(IdfplusParser.LogicExpContext context)
Expand All @@ -73,8 +78,7 @@ public override Expression VisitLogicExp(IdfplusParser.LogicExpContext context)
return new BooleanExpression(conditional[context.op.Text](lhsBooleanExp.Value, rhsBooleanExp.Value));
}

throw new NotImplementedException(
$"The logic expression is not defined for types {lhs.GetType()} and {rhs.GetType()}.");
throw new NotImplementedException($"Line {context.Start.Line}: The logic expression is not defined for types {lhs.GetType()} and {rhs.GetType()}.");
}

public override Expression VisitNumericExp(IdfplusParser.NumericExpContext context)
Expand Down Expand Up @@ -159,21 +163,30 @@ public override Expression VisitAddSub(IdfplusParser.AddSubContext context)
return new StringExpression(lhsString.Text + rhsString.Text);

throw new NotImplementedException(
$"The operation of {op} with types {lhs.GetType()} and {rhs.GetType()} is not defined.");
$"Line {context.Start.Line}: The operation of {op} with types {lhs.GetType()} and {rhs.GetType()} is not defined.");
}

public override Expression VisitFunctionExp(IdfplusParser.FunctionExpContext functionExpContext)
{
Expression func = Visit(functionExpContext.funcexp);

if (!(func is FunctionExpression functionExpression))
throw new InvalidOperationException("Attempt to apply function to non function application.");
throw new InvalidOperationException($"Line {functionExpContext.Start.Line}: Attempt to apply function to non function application.");

// string functionName = functionExpContext.function_application().IDENTIFIER().GetText();
// IFunction function = _functions[functionName];
//
var expressions = functionExpContext.expression().Skip(1).Select(Visit).ToList();
(string text, Expression expression) = functionExpression.Evaluate(expressions);
string text;
Expression expression;
try
{
var expressions = functionExpContext.expression().Skip(1).Select(Visit).ToList();
(text, expression) = functionExpression.Evaluate(expressions);
}
catch (Exception exception)
{
throw new Exception($"Line {functionExpContext.Start.Line}: {exception.Message}");
}

output.Append(text);

Expand Down
4 changes: 4 additions & 0 deletions src/IdfPlusVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public IdfPlusVisitor()
};
_environments[0]["map"] = new MapFunctionExpression();
_environments[0]["load"] = new LoadFunctionExpression();
_environments[0]["head"] = new ListHeadFunctionExpression();
_environments[0]["tail"] = new ListTailFunctionExpression();
_environments[0]["index"] = new ListIndexFunctionExpression();
_environments[0]["length"] = new ListLengthFunctionExpression();
}

public IdfPlusVisitor(List<Dictionary<string, Expression>> environments)
Expand Down
87 changes: 87 additions & 0 deletions src/ListFunctionExpressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace src
{
public class ListHeadFunctionExpression : FunctionExpression
{
public ListHeadFunctionExpression() : base(new List<Dictionary<string, Expression>>(), new List<string> { "list" })
{
}

public override (string, Expression) Evaluate(List<Expression> inputs)
{
if (inputs.Count != 1) throw new ArgumentException($"'head' expects one parameter, saw {inputs.Count}");
if (!(inputs[0] is ListExpression listExpression)) throw new NotImplementedException($"'head' is not defined for types other than list.");
if (listExpression.Expressions.Any()) return ("", listExpression.Expressions[0]);
throw new IndexOutOfRangeException($"The list has no elements.");
}
}

public class ListTailFunctionExpression : FunctionExpression
{
public ListTailFunctionExpression() : base(new List<Dictionary<string, Expression>>(), new List<string> { "list" })
{
}

public override (string, Expression) Evaluate(List<Expression> inputs)
{
if (inputs.Count != 1) throw new ArgumentException($"'tail' expects one parameter, saw {inputs.Count}");
if (!(inputs[0] is ListExpression listExpression)) throw new NotImplementedException($"'tail' is not defined for types other than list.");
if (!listExpression.Expressions.Any()) throw new IndexOutOfRangeException($"The list has no elements.");
return ("", new ListExpression(listExpression.Expressions.Skip(1).ToList()));
}
}

public class ListIndexFunctionExpression : FunctionExpression
{
public ListIndexFunctionExpression() : base(new List<Dictionary<string, Expression>>(), new List<string> { "list", "index" })
{
}

public override (string, Expression) Evaluate(List<Expression> inputs)
{
if (inputs.Count != 2)
throw new ArgumentException(
$"'index' expects 2 parameters: a list and a numeric, saw {inputs.Count} parameters");
if (!(inputs[0] is ListExpression listExpression)) throw new NotImplementedException($"First parameter of 'index' should be a list");
if (!(inputs[1] is NumericExpression numericExpression))
throw new NotImplementedException("Second parameter of 'index' should be a numeric");
if (!listExpression.Expressions.Any()) throw new IndexOutOfRangeException($"The list has no elements.");

int index = Convert.ToInt32(numericExpression.Value);

if (index >= 0)
{
if (index > listExpression.Expressions.Count)
{
throw new IndexOutOfRangeException($"Attempted to access index {index} of list. List only has {listExpression.Expressions.Count} items.");
}

return ("", listExpression.Expressions[index]);
}

if (Math.Abs(index) > listExpression.Expressions.Count)
{
throw new IndexOutOfRangeException($"Attempted to access index {index} of list. List only has {listExpression.Expressions.Count} items.");
}

return ("", listExpression.Expressions[index + listExpression.Expressions.Count]);
}
}

public class ListLengthFunctionExpression : FunctionExpression
{
public ListLengthFunctionExpression() : base(new List<Dictionary<string, Expression>>(), new List<string> { "list" })
{
}

public override (string, Expression) Evaluate(List<Expression> inputs)
{
if (inputs.Count != 1) throw new ArgumentException( $"'length' expects 1 list parameter, saw {inputs.Count} parameters.");
if (!(inputs[0] is ListExpression listExpression)) throw new ArgumentException( $"'length' expects a list as the parameter.");
return ("", new NumericExpression(listExpression.Expressions.Count));
}
}
}

0 comments on commit aa56862

Please sign in to comment.