diff --git a/src/function/builtin/special/IF.java b/src/function/builtin/special/IF.java new file mode 100644 index 0000000..6d89684 --- /dev/null +++ b/src/function/builtin/special/IF.java @@ -0,0 +1,52 @@ +package function.builtin.special; + +import static function.builtin.EVAL.eval; + +import function.*; +import sexpression.*; + +public class IF extends LispFunction { + + private ArgumentValidator argumentValidator; + + public IF() { + this.argumentValidator = new ArgumentValidator("IF"); + this.argumentValidator.setMinimumNumberOfArguments(2); + this.argumentValidator.setMaximumNumberOfArguments(3); + } + + public SExpression call(Cons argumentList) { + argumentValidator.validate(argumentList); + + SExpression test = eval(argumentList.getCar()); + SExpression thenForm = getThenForm(argumentList); + SExpression elseForm = getElseForm(argumentList); + + return isNil(test) ? eval(elseForm) : eval(thenForm); + } + + private boolean isNil(SExpression test) { + return test.nullp(); + } + + private SExpression getThenForm(Cons argumentList) { + Cons expressions = getRestOfList(argumentList); + + return expressions.getCar(); + } + + private Cons getRestOfList(Cons argumentList) { + return (Cons) argumentList.getCdr(); + } + + private SExpression getElseForm(Cons argumentList) { + Cons expressions = getRestOfList(argumentList); + + return getRestOfList(expressions).getCar(); + } + + public boolean evaluateArguments() { + return false; + } + +} diff --git a/src/table/FunctionTable.java b/src/table/FunctionTable.java index 60dd539..fc70e18 100644 --- a/src/table/FunctionTable.java +++ b/src/table/FunctionTable.java @@ -60,6 +60,7 @@ public class FunctionTable { functionTable.put("FIRST", new CAR()); functionTable.put("FUNCALL", new FUNCALL()); functionTable.put("GREATERP", new GREATERP()); + functionTable.put("IF", new IF()); functionTable.put("LAMBDA", new LAMBDA()); functionTable.put("LENGTH", new LENGTH()); functionTable.put("LET", new LET()); diff --git a/test/function/builtin/special/CONDTester.java b/test/function/builtin/special/CONDTester.java index acd7063..6a16f86 100644 --- a/test/function/builtin/special/CONDTester.java +++ b/test/function/builtin/special/CONDTester.java @@ -22,6 +22,13 @@ public class CONDTester { assertSExpressionsMatch(parseString("T"), evaluateString(input)); } + @Test + public void testCondWithNumber() { + String input = "(cond ((+ 1 2)))"; + + assertSExpressionsMatch(parseString("3"), evaluateString(input)); + } + @Test public void testCondWithSingleExpression() { String input = "(cond (T \"true\"))"; diff --git a/test/function/builtin/special/IFTester.java b/test/function/builtin/special/IFTester.java new file mode 100644 index 0000000..03179a4 --- /dev/null +++ b/test/function/builtin/special/IFTester.java @@ -0,0 +1,91 @@ +package function.builtin.special; + +import static testutil.TestUtilities.evaluateString; +import static testutil.TypeAssertions.*; + +import org.junit.*; + +import function.ArgumentValidator.*; +import function.builtin.EVAL.UndefinedSymbolException; +import table.ExecutionContext; + +public class IFTester { + + private ExecutionContext executionContext; + + public IFTester() { + this.executionContext = ExecutionContext.getInstance(); + } + + @Before + public void setUp() { + executionContext.clearContext(); + } + + @After + public void tearDown() { + executionContext.clearContext(); + } + + @Test + public void ifWithOneExpression_ReturnsExpression() { + String input = "(if t t)"; + + assertT(evaluateString(input)); + } + + @Test + public void ifWithOneExpression_ReturnsNil() { + String input = "(if nil t)"; + + assertNil(evaluateString(input)); + } + + @Test + public void ifWithTwoExpressions_ReturnsFirst() { + String input = "(if t t nil)"; + + assertT(evaluateString(input)); + } + + @Test + public void ifWithTwoExpressions_ReturnsSecond() { + String input = "(if nil nil t)"; + + assertT(evaluateString(input)); + } + + @Test + public void ifWithNumericConditional() { + String input = "(if 23 t nil)"; + + assertT(evaluateString(input)); + } + + @Test(expected = UndefinedSymbolException.class) + public void ifWithNilCondition_DoesNotEvaluateThenForm() { + String input = "(if nil (setf x 22))"; + + assertNil(evaluateString(input)); + evaluateString("x"); + } + + @Test(expected = UndefinedSymbolException.class) + public void ifWithTrueCondition_DoesNotEvaluateElseForm() { + String input = "(if t nil (setf x 22))"; + + assertNil(evaluateString(input)); + evaluateString("x"); + } + + @Test(expected = TooFewArgumentsException.class) + public void ifWithTooFewArguments() { + evaluateString("(if t)"); + } + + @Test(expected = TooManyArgumentsException.class) + public void ifWithTooManyArguments() { + evaluateString("(if t t t t)"); + } + +}