Resolves #7 - Allow prefixing of numbers
This commit is contained in:
parent
bf40feadec
commit
fb35884479
|
@ -2,13 +2,14 @@
|
||||||
Test
|
Test
|
||||||
---
|
---
|
||||||
| script | lisp interpreter fixture |
|
| script | lisp interpreter fixture |
|
||||||
| evaluate | (defun adderx (x) (lambda (y) (+ x y))) |
|
| show | evaluate | (defun adderx (x) (lambda (y) (+ x y))) |
|
||||||
| evaluate | (setf adder20 (adderx 20)) |
|
| show | evaluate | (setf adder20 (adderx 20)) |
|
||||||
| check | evaluate | (funcall adder20 2) | 22 |
|
| check | evaluate | (funcall adder20 2) | 22 |
|
||||||
|
|
||||||
|
|
||||||
| script | lisp interpreter fixture |
|
| script | lisp interpreter fixture |
|
||||||
| evaluate |{{{!-
|
| # | Let Over Lambda Over Let Over Lambda |
|
||||||
|
| show | evaluate |!-
|
||||||
|
|
||||||
(let ((direction 'up))
|
(let ((direction 'up))
|
||||||
(defun toggle-counter-direction ()
|
(defun toggle-counter-direction ()
|
||||||
|
@ -23,16 +24,16 @@ Test
|
||||||
(if (eq direction 'up)
|
(if (eq direction 'up)
|
||||||
(setq counter (+ counter 1))
|
(setq counter (+ counter 1))
|
||||||
(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) | 1 |
|
||||||
| check | evaluate | (funcall my-counter) | 2 |
|
| check | evaluate | (funcall my-counter) | 2 |
|
||||||
| check | evaluate | (funcall my-counter) | 3 |
|
| 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) | 2 |
|
||||||
| check | evaluate | (funcall my-counter) | 1 |
|
| check | evaluate | (funcall my-counter) | 1 |
|
||||||
| check | evaluate | (funcall my-counter) | 0 |
|
| 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) | 1 |
|
||||||
| check | evaluate | (funcall my-counter) | 2 |
|
| check | evaluate | (funcall my-counter) | 2 |
|
||||||
| check | evaluate | (funcall my-counter) | 3 |
|
| check | evaluate | (funcall my-counter) | 3 |
|
|
@ -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.TestClosure||11:24:27 Mon, Feb 27, 2017|
|
||||||
|LispInterpreter.TestOne||09:26:08 Fri, Feb 24, 2017|
|
|LispInterpreter.TestOne||09:26:08 Fri, Feb 24, 2017|
|
||||||
|LispInterpreter.SuiteSetUp||14:27:52 Wed, Feb 22, 2017|
|
|LispInterpreter.SuiteSetUp||14:27:52 Wed, Feb 22, 2017|
|
||||||
|
|
|
@ -29,19 +29,19 @@
|
||||||
)
|
)
|
||||||
|
|
||||||
(defun one-year-with-negative-interest-rate ()
|
(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 ()
|
(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 ()
|
(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 ()
|
(defun four-years-with-negative-interest-rate ()
|
||||||
(assert= 81450 (compound-interest 100000 (- 5) 4))
|
(assert= 81450 (compound-interest 100000 -5 4))
|
||||||
)
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package scanner;
|
package scanner;
|
||||||
|
|
||||||
|
import static java.lang.Character.*;
|
||||||
import static util.Characters.*;
|
import static util.Characters.*;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -30,7 +31,7 @@ public class LispScanner {
|
||||||
char currentCharacter = (char) c;
|
char currentCharacter = (char) c;
|
||||||
positionTracker.incrementColumn();
|
positionTracker.incrementColumn();
|
||||||
|
|
||||||
if (!Character.isWhitespace(currentCharacter))
|
if (!isWhitespace(currentCharacter))
|
||||||
return createTokenFromCharacter(currentCharacter);
|
return createTokenFromCharacter(currentCharacter);
|
||||||
else if (currentCharacter == NEWLINE)
|
else if (currentCharacter == NEWLINE)
|
||||||
positionTracker.incrementLine();
|
positionTracker.incrementLine();
|
||||||
|
@ -51,9 +52,11 @@ public class LispScanner {
|
||||||
|
|
||||||
if (firstCharacter == DOUBLE_QUOTE)
|
if (firstCharacter == DOUBLE_QUOTE)
|
||||||
tokenText = retrieveStringTokenText(firstCharacter);
|
tokenText = retrieveStringTokenText(firstCharacter);
|
||||||
else if (Character.isDigit(firstCharacter))
|
else if (isNumberPrefix(firstCharacter))
|
||||||
|
tokenText = retrieveNumberOrIdentifierTokenText(firstCharacter);
|
||||||
|
else if (isDigit(firstCharacter))
|
||||||
tokenText = retrieveNumberTokenText(firstCharacter);
|
tokenText = retrieveNumberTokenText(firstCharacter);
|
||||||
else if (Characters.isLegalIdentifierCharacter(firstCharacter))
|
else if (isLegalIdentifierCharacter(firstCharacter))
|
||||||
tokenText = retrieveIdentifierTokenText(firstCharacter);
|
tokenText = retrieveIdentifierTokenText(firstCharacter);
|
||||||
|
|
||||||
return tokenText;
|
return tokenText;
|
||||||
|
@ -66,8 +69,18 @@ public class LispScanner {
|
||||||
return retriever.retrieveToken();
|
return retriever.retrieveToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String retrieveNumberTokenText(char firstDigit) {
|
private String retrieveNumberOrIdentifierTokenText(char firstCharacter) {
|
||||||
ComplexTokenTextRetriever retriever = new ComplexTokenTextRetriever(firstDigit, Character::isDigit);
|
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();
|
return retriever.retrieveToken();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package token;
|
package token;
|
||||||
|
|
||||||
|
import static java.lang.Character.isDigit;
|
||||||
import static util.Characters.*;
|
import static util.Characters.*;
|
||||||
|
|
||||||
import file.FilePosition;
|
import file.FilePosition;
|
||||||
import util.Characters;
|
|
||||||
|
|
||||||
public class TokenFactoryImpl implements TokenFactory {
|
public class TokenFactoryImpl implements TokenFactory {
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ public class TokenFactoryImpl implements TokenFactory {
|
||||||
case DOUBLE_QUOTE:
|
case DOUBLE_QUOTE:
|
||||||
return new QuotedString(text, position);
|
return new QuotedString(text, position);
|
||||||
default:
|
default:
|
||||||
if (Character.isDigit(firstCharacter)) {
|
if (isNumeric(firstCharacter, text)) {
|
||||||
return new Number(text, position);
|
return new Number(text, position);
|
||||||
} else if (Characters.isLegalIdentifierCharacter(firstCharacter)) {
|
} else if (isLegalIdentifierCharacter(firstCharacter)) {
|
||||||
return new Identifier(text, position);
|
return new Identifier(text, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,18 @@ public class TokenFactoryImpl implements TokenFactory {
|
||||||
throw new BadCharacterException(text, position);
|
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) {
|
public Token createEOFToken(FilePosition position) {
|
||||||
return new Eof("EOF", position);
|
return new Eof("EOF", position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,14 @@ public final class Characters {
|
||||||
public static final int EOF = -1;
|
public static final int EOF = -1;
|
||||||
|
|
||||||
public static final char BACKSLASH = '\\';
|
public static final char BACKSLASH = '\\';
|
||||||
|
public static final char DASH = '-';
|
||||||
public static final char DOUBLE_QUOTE = '\"';
|
public static final char DOUBLE_QUOTE = '\"';
|
||||||
public static final char HASH = '#';
|
public static final char HASH = '#';
|
||||||
public static final char LEFT_PARENTHESIS = '(';
|
public static final char LEFT_PARENTHESIS = '(';
|
||||||
public static final char LEFT_SQUARE_BRACKET = '[';
|
public static final char LEFT_SQUARE_BRACKET = '[';
|
||||||
public static final char NEWLINE = '\n';
|
public static final char NEWLINE = '\n';
|
||||||
public static final char PERIOD = '.';
|
public static final char PERIOD = '.';
|
||||||
|
public static final char PLUS = '+';
|
||||||
public static final char RIGHT_PARENTHESIS = ')';
|
public static final char RIGHT_PARENTHESIS = ')';
|
||||||
public static final char RIGHT_SQUARE_BRACKET = ']';
|
public static final char RIGHT_SQUARE_BRACKET = ']';
|
||||||
public static final char SEMICOLON = ';';
|
public static final char SEMICOLON = ';';
|
||||||
|
@ -45,4 +47,8 @@ public final class Characters {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isNumberPrefix(char c) {
|
||||||
|
return c == DASH || c == PLUS;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,6 +161,15 @@ public class LispScannerTypeTester {
|
||||||
assertTokenTypesMatch(input);
|
assertTokenTypesMatch(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenPrefixedIdentifiers_ReturnsCorrectTypes() {
|
||||||
|
String input = "-a +b";
|
||||||
|
expectedTypes.add(Identifier.class);
|
||||||
|
expectedTypes.add(Identifier.class);
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenSingleDigitNumber_ReturnsCorrectTypes() {
|
public void givenSingleDigitNumber_ReturnsCorrectTypes() {
|
||||||
String input = "1";
|
String input = "1";
|
||||||
|
@ -169,6 +178,58 @@ public class LispScannerTypeTester {
|
||||||
assertTokenTypesMatch(input);
|
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
|
@Test
|
||||||
public void givenMultipleDigitNumber_ReturnsCorrectTypes() {
|
public void givenMultipleDigitNumber_ReturnsCorrectTypes() {
|
||||||
String input = "1234567890";
|
String input = "1234567890";
|
||||||
|
|
Loading…
Reference in New Issue