From fb35884479d52ab3076e62cb2f16fcba26655112 Mon Sep 17 00:00:00 2001 From: Mike Cifelli Date: Mon, 27 Feb 2017 13:34:04 -0500 Subject: [PATCH] Resolves #7 - Allow prefixing of numbers --- .../LispInterpreter/LexicalClosures.wiki | 15 ++--- fitnesse/FitNesseRoot/RecentChanges.wiki | 2 +- lisp/compound-interest-test.lisp | 8 +-- src/scanner/LispScanner.java | 23 +++++-- src/token/TokenFactoryImpl.java | 18 +++++- src/util/Characters.java | 6 ++ test/scanner/LispScannerTypeTester.java | 61 +++++++++++++++++++ 7 files changed, 113 insertions(+), 20 deletions(-) diff --git a/fitnesse/FitNesseRoot/LispInterpreter/LexicalClosures.wiki b/fitnesse/FitNesseRoot/LispInterpreter/LexicalClosures.wiki index 74d0a3c..db70e13 100644 --- a/fitnesse/FitNesseRoot/LispInterpreter/LexicalClosures.wiki +++ b/fitnesse/FitNesseRoot/LispInterpreter/LexicalClosures.wiki @@ -2,13 +2,14 @@ Test --- | script | lisp interpreter fixture | -| evaluate | (defun adderx (x) (lambda (y) (+ x y))) | -| evaluate | (setf adder20 (adderx 20)) | +| show | evaluate | (defun adderx (x) (lambda (y) (+ x y))) | +| show | evaluate | (setf adder20 (adderx 20)) | | check | evaluate | (funcall adder20 2) | 22 | | script | lisp interpreter fixture | -| evaluate |{{{!- +| # | Let Over Lambda Over Let Over Lambda | +| show | evaluate |!- (let ((direction 'up)) (defun toggle-counter-direction () @@ -23,16 +24,16 @@ Test (if (eq direction 'up) (setq counter (+ counter 1)) (setq counter (- counter 1))))))) - -!}}}| -| evaluate | (setq my-counter (counter-class)) | + -!| +| show | evaluate | (setq my-counter (counter-class)) | | check | evaluate | (funcall my-counter) | 1 | | check | evaluate | (funcall my-counter) | 2 | | check | evaluate | (funcall my-counter) | 3 | -| evaluate | (toggle-counter-direction) | +| show | evaluate | (toggle-counter-direction) | | check | evaluate | (funcall my-counter) | 2 | | check | evaluate | (funcall my-counter) | 1 | | check | evaluate | (funcall my-counter) | 0 | -| evaluate | (toggle-counter-direction) | +| show | evaluate | (toggle-counter-direction) | | check | evaluate | (funcall my-counter) | 1 | | check | evaluate | (funcall my-counter) | 2 | | check | evaluate | (funcall my-counter) | 3 | \ No newline at end of file diff --git a/fitnesse/FitNesseRoot/RecentChanges.wiki b/fitnesse/FitNesseRoot/RecentChanges.wiki index 59ef060..a85c5af 100644 --- a/fitnesse/FitNesseRoot/RecentChanges.wiki +++ b/fitnesse/FitNesseRoot/RecentChanges.wiki @@ -1,4 +1,4 @@ -|LispInterpreter.LexicalClosures||11:52:29 Mon, Feb 27, 2017| +|LispInterpreter.LexicalClosures||12:10:13 Mon, Feb 27, 2017| |LispInterpreter.TestClosure||11:24:27 Mon, Feb 27, 2017| |LispInterpreter.TestOne||09:26:08 Fri, Feb 24, 2017| |LispInterpreter.SuiteSetUp||14:27:52 Wed, Feb 22, 2017| diff --git a/lisp/compound-interest-test.lisp b/lisp/compound-interest-test.lisp index 1031947..0f92231 100644 --- a/lisp/compound-interest-test.lisp +++ b/lisp/compound-interest-test.lisp @@ -29,19 +29,19 @@ ) (defun one-year-with-negative-interest-rate () - (assert= 95000 (compound-interest 100000 (- 5) 1)) + (assert= 95000 (compound-interest 100000 -5 1)) ) (defun two-years-with-negative-interest-rate () - (assert= 90250 (compound-interest 100000 (- 5) 2)) + (assert= 90250 (compound-interest 100000 -5 2)) ) (defun three-years-with-negative-interest-rate () - (assert= 85737 (compound-interest 100000 (- 5) 3)) + (assert= 85737 (compound-interest 100000 -5 3)) ) (defun four-years-with-negative-interest-rate () - (assert= 81450 (compound-interest 100000 (- 5) 4)) + (assert= 81450 (compound-interest 100000 -5 4)) ) ) diff --git a/src/scanner/LispScanner.java b/src/scanner/LispScanner.java index 1b0c899..277f1a9 100644 --- a/src/scanner/LispScanner.java +++ b/src/scanner/LispScanner.java @@ -1,5 +1,6 @@ package scanner; +import static java.lang.Character.*; import static util.Characters.*; import java.io.InputStream; @@ -30,7 +31,7 @@ public class LispScanner { char currentCharacter = (char) c; positionTracker.incrementColumn(); - if (!Character.isWhitespace(currentCharacter)) + if (!isWhitespace(currentCharacter)) return createTokenFromCharacter(currentCharacter); else if (currentCharacter == NEWLINE) positionTracker.incrementLine(); @@ -51,9 +52,11 @@ public class LispScanner { if (firstCharacter == DOUBLE_QUOTE) tokenText = retrieveStringTokenText(firstCharacter); - else if (Character.isDigit(firstCharacter)) + else if (isNumberPrefix(firstCharacter)) + tokenText = retrieveNumberOrIdentifierTokenText(firstCharacter); + else if (isDigit(firstCharacter)) tokenText = retrieveNumberTokenText(firstCharacter); - else if (Characters.isLegalIdentifierCharacter(firstCharacter)) + else if (isLegalIdentifierCharacter(firstCharacter)) tokenText = retrieveIdentifierTokenText(firstCharacter); return tokenText; @@ -66,8 +69,18 @@ public class LispScanner { return retriever.retrieveToken(); } - private String retrieveNumberTokenText(char firstDigit) { - ComplexTokenTextRetriever retriever = new ComplexTokenTextRetriever(firstDigit, Character::isDigit); + private String retrieveNumberOrIdentifierTokenText(char firstCharacter) { + char nextCharacter = (char) inputStream.read(); + inputStream.unreadLastCharacter(); + + if (isDigit(nextCharacter)) + return retrieveNumberTokenText(firstCharacter); + + return retrieveIdentifierTokenText(firstCharacter); + } + + private String retrieveNumberTokenText(char firstCharacter) { + ComplexTokenTextRetriever retriever = new ComplexTokenTextRetriever(firstCharacter, Character::isDigit); return retriever.retrieveToken(); } diff --git a/src/token/TokenFactoryImpl.java b/src/token/TokenFactoryImpl.java index 14c8a84..c153580 100644 --- a/src/token/TokenFactoryImpl.java +++ b/src/token/TokenFactoryImpl.java @@ -1,9 +1,9 @@ package token; +import static java.lang.Character.isDigit; import static util.Characters.*; import file.FilePosition; -import util.Characters; public class TokenFactoryImpl implements TokenFactory { @@ -23,9 +23,9 @@ public class TokenFactoryImpl implements TokenFactory { case DOUBLE_QUOTE: return new QuotedString(text, position); default: - if (Character.isDigit(firstCharacter)) { + if (isNumeric(firstCharacter, text)) { return new Number(text, position); - } else if (Characters.isLegalIdentifierCharacter(firstCharacter)) { + } else if (isLegalIdentifierCharacter(firstCharacter)) { return new Identifier(text, position); } } @@ -33,6 +33,18 @@ public class TokenFactoryImpl implements TokenFactory { throw new BadCharacterException(text, position); } + private boolean isNumeric(char firstCharacter, String text) { + return isDigit(firstCharacter) || isPrefixedNumeric(firstCharacter, text); + } + + private boolean isPrefixedNumeric(char firstCharacter, String text) { + return isNumberPrefix(firstCharacter) && isNextCharacterDigit(text); + } + + private boolean isNextCharacterDigit(String text) { + return (text.length() > 1) && isDigit(text.charAt(1)); + } + public Token createEOFToken(FilePosition position) { return new Eof("EOF", position); } diff --git a/src/util/Characters.java b/src/util/Characters.java index d6b5f45..90e9bd2 100644 --- a/src/util/Characters.java +++ b/src/util/Characters.java @@ -9,12 +9,14 @@ public final class Characters { public static final int EOF = -1; public static final char BACKSLASH = '\\'; + public static final char DASH = '-'; public static final char DOUBLE_QUOTE = '\"'; public static final char HASH = '#'; public static final char LEFT_PARENTHESIS = '('; public static final char LEFT_SQUARE_BRACKET = '['; public static final char NEWLINE = '\n'; public static final char PERIOD = '.'; + public static final char PLUS = '+'; public static final char RIGHT_PARENTHESIS = ')'; public static final char RIGHT_SQUARE_BRACKET = ']'; public static final char SEMICOLON = ';'; @@ -44,5 +46,9 @@ public final class Characters { public static boolean isLegalStringCharacter(char c) { return true; } + + public static boolean isNumberPrefix(char c) { + return c == DASH || c == PLUS; + } } diff --git a/test/scanner/LispScannerTypeTester.java b/test/scanner/LispScannerTypeTester.java index 7f4f8f1..d6fb649 100644 --- a/test/scanner/LispScannerTypeTester.java +++ b/test/scanner/LispScannerTypeTester.java @@ -161,6 +161,15 @@ public class LispScannerTypeTester { assertTokenTypesMatch(input); } + @Test + public void givenPrefixedIdentifiers_ReturnsCorrectTypes() { + String input = "-a +b"; + expectedTypes.add(Identifier.class); + expectedTypes.add(Identifier.class); + + assertTokenTypesMatch(input); + } + @Test public void givenSingleDigitNumber_ReturnsCorrectTypes() { String input = "1"; @@ -169,6 +178,58 @@ public class LispScannerTypeTester { assertTokenTypesMatch(input); } + @Test + public void givenPrefixedNegativeNumber_ReturnsCorrectTypes() { + String input = "-1"; + expectedTypes.add(Number.class); + + assertTokenTypesMatch(input); + } + + @Test + public void givenPrefixedNumber_ReturnsCorrectTypes() { + String input = "+1"; + expectedTypes.add(Number.class); + + assertTokenTypesMatch(input); + } + + @Test + public void givenPrefixedNegativeNumberAndIdentifier_ReturnsCorrectTypes() { + String input = "-1apple"; + expectedTypes.add(Number.class); + expectedTypes.add(Identifier.class); + + assertTokenTypesMatch(input); + } + + @Test + public void givenPrefixedNumberAndIdentifier_ReturnsCorrectTypes() { + String input = "+1apple"; + expectedTypes.add(Number.class); + expectedTypes.add(Identifier.class); + + assertTokenTypesMatch(input); + } + + @Test + public void givenPrefixedNegativeNumberAndString_ReturnsCorrectTypes() { + String input = "-1\"apple\""; + expectedTypes.add(Number.class); + expectedTypes.add(QuotedString.class); + + assertTokenTypesMatch(input); + } + + @Test + public void givenPrefixedNumberAndString_ReturnsCorrectTypes() { + String input = "+1\"apple\""; + expectedTypes.add(Number.class); + expectedTypes.add(QuotedString.class); + + assertTokenTypesMatch(input); + } + @Test public void givenMultipleDigitNumber_ReturnsCorrectTypes() { String input = "1234567890";