Added unit tests for LispScanner and refactored LispFilterInputStream
This commit is contained in:
parent
daf35a72fa
commit
702c11a7b9
|
@ -126,7 +126,7 @@ public class LispParser {
|
||||||
return sExpr();
|
return sExpr();
|
||||||
}
|
}
|
||||||
|
|
||||||
// sExpr ::= NUMBER | IDENTIFIER | RESERVED | STRING | QUOTE_MARK sExpr |
|
// sExpr ::= NUMBER | IDENTIFIER | STRING | QUOTE_MARK sExpr |
|
||||||
// LEFT_PAREN sExprTail
|
// LEFT_PAREN sExprTail
|
||||||
//
|
//
|
||||||
// Returns: an S-expression that matches the rules given above
|
// Returns: an S-expression that matches the rules given above
|
||||||
|
@ -141,7 +141,6 @@ public class LispParser {
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
return new LispNumber(nextToken.getText());
|
return new LispNumber(nextToken.getText());
|
||||||
case IDENTIFIER:
|
case IDENTIFIER:
|
||||||
case RESERVED:
|
|
||||||
return new Symbol(nextToken.getText());
|
return new Symbol(nextToken.getText());
|
||||||
case STRING:
|
case STRING:
|
||||||
return new LispString(nextToken.getText());
|
return new LispString(nextToken.getText());
|
||||||
|
|
|
@ -9,44 +9,55 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class LispFilterInputStream extends FilterInputStream {
|
public class LispFilterInputStream extends FilterInputStream {
|
||||||
|
|
||||||
private boolean inQuote;
|
private boolean inQuotedString;
|
||||||
|
private int previousCharacter;
|
||||||
private int nextCharacter;
|
private int nextCharacter;
|
||||||
|
|
||||||
public LispFilterInputStream(InputStream in) {
|
public LispFilterInputStream(InputStream underlyingInputStream) {
|
||||||
super(in);
|
super(underlyingInputStream);
|
||||||
inQuote = false;
|
|
||||||
|
inQuotedString = false;
|
||||||
|
previousCharacter = 0;
|
||||||
nextCharacter = 0;
|
nextCharacter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
nextCharacter = super.read();
|
readNextCharacter();
|
||||||
|
|
||||||
if (haveEnteredComment()) {
|
if (haveEnteredComment())
|
||||||
consumeAllBytesInComment();
|
consumeAllBytesInComment();
|
||||||
} else if (haveEncounteredStringBoundary()) {
|
|
||||||
inQuote = (!inQuote);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nextCharacter;
|
return nextCharacter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void readNextCharacter() throws IOException {
|
||||||
|
previousCharacter = nextCharacter;
|
||||||
|
nextCharacter = super.read();
|
||||||
|
|
||||||
|
indicateEncounterWithStringBoundary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void indicateEncounterWithStringBoundary() {
|
||||||
|
if (haveEncounteredStringBoundary())
|
||||||
|
inQuotedString = !inQuotedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean haveEncounteredStringBoundary() {
|
||||||
|
return (previousCharacter != '\\') && (nextCharacter == '\"');
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean haveEnteredComment() {
|
||||||
|
return (nextCharacter == ';') && (!inQuotedString);
|
||||||
|
}
|
||||||
|
|
||||||
private void consumeAllBytesInComment() throws IOException {
|
private void consumeAllBytesInComment() throws IOException {
|
||||||
while (stillInComment()) {
|
while (stillInComment())
|
||||||
nextCharacter = super.read();
|
nextCharacter = super.read();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean stillInComment() {
|
private boolean stillInComment() {
|
||||||
return (nextCharacter != '\n') && (nextCharacter != -1);
|
return (nextCharacter != '\n') && (nextCharacter != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean haveEnteredComment() {
|
|
||||||
return (nextCharacter == ';') && (!inQuote);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean haveEncounteredStringBoundary() {
|
|
||||||
return nextCharacter == '\"';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,6 @@ public class Token {
|
||||||
/** A number token */
|
/** A number token */
|
||||||
NUMBER,
|
NUMBER,
|
||||||
|
|
||||||
/** A reserved word token */
|
|
||||||
RESERVED,
|
|
||||||
|
|
||||||
/** An identifier token */
|
/** An identifier token */
|
||||||
IDENTIFIER,
|
IDENTIFIER,
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
<body>
|
<body>
|
||||||
Provides the classes necessary to perform a lexical analysis of the Lisp
|
Provides the classes necessary to perform a lexical analysis of the Lisp programming language.
|
||||||
programming language.
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -2,20 +2,28 @@ package scanner;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import testutils.TestUtilities;
|
||||||
|
|
||||||
public class LispFilterInputStreamTester {
|
public class LispFilterInputStreamTester {
|
||||||
|
|
||||||
|
private StringBuilder stringBuilder;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
stringBuilder = new StringBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void oneComment_Removed() throws IOException {
|
public void oneComment_Removed() throws IOException {
|
||||||
String input = ";comment";
|
String input = ";comment";
|
||||||
String expectedResult = "";
|
String expectedResult = "";
|
||||||
|
|
||||||
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +31,7 @@ public class LispFilterInputStreamTester {
|
||||||
public void multipleComments_Removed() throws IOException {
|
public void multipleComments_Removed() throws IOException {
|
||||||
String input = ";comment1\n;comment2\n;comment3";
|
String input = ";comment1\n;comment2\n;comment3";
|
||||||
String expectedResult = "\n\n";
|
String expectedResult = "\n\n";
|
||||||
|
|
||||||
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +39,7 @@ public class LispFilterInputStreamTester {
|
||||||
public void nil_NotRemoved() throws IOException {
|
public void nil_NotRemoved() throws IOException {
|
||||||
String input = "()";
|
String input = "()";
|
||||||
String expectedResult = "()";
|
String expectedResult = "()";
|
||||||
|
|
||||||
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,18 +47,28 @@ public class LispFilterInputStreamTester {
|
||||||
public void interiorComment_Removed() throws IOException {
|
public void interiorComment_Removed() throws IOException {
|
||||||
String input = "(;this is a comment\n)";
|
String input = "(;this is a comment\n)";
|
||||||
String expectedResult = "(\n)";
|
String expectedResult = "(\n)";
|
||||||
|
|
||||||
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void commentInString_NotRemoved() throws IOException {
|
public void commentInString_NotRemoved() throws IOException {
|
||||||
String input = "\"string;this should remain\"";
|
String input = "\"string;this should remain\"";
|
||||||
|
|
||||||
assertEquals(input, getLispFilterInputStreamResult(input));
|
assertEquals(input, getLispFilterInputStreamResult(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void commentInStringWithNewline_NotRemoved() throws IOException {
|
public void commentInStringWithNewline_NotRemoved() throws IOException {
|
||||||
String input = "\"string;this should\n remain\"";
|
String input = "\"string;this should\n remain\"";
|
||||||
|
|
||||||
|
assertEquals(input, getLispFilterInputStreamResult(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void commentInStringWithEscapedDoubleQuote_NotRemoved() throws IOException {
|
||||||
|
String input = "\"string \\\" ;this should remain\"";
|
||||||
|
|
||||||
assertEquals(input, getLispFilterInputStreamResult(input));
|
assertEquals(input, getLispFilterInputStreamResult(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,22 +76,18 @@ public class LispFilterInputStreamTester {
|
||||||
public void manyCommentsWithStatements_OnlyCommentsRemoved() throws IOException {
|
public void manyCommentsWithStatements_OnlyCommentsRemoved() throws IOException {
|
||||||
String input = ";first comment \n '(1 2 3) \n ;second comment \n (defun add1 (x) (+ x 1)) ;third comment";
|
String input = ";first comment \n '(1 2 3) \n ;second comment \n (defun add1 (x) (+ x 1)) ;third comment";
|
||||||
String expectedResult = "\n '(1 2 3) \n \n (defun add1 (x) (+ x 1)) ";
|
String expectedResult = "\n '(1 2 3) \n \n (defun add1 (x) (+ x 1)) ";
|
||||||
|
|
||||||
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
assertEquals(expectedResult, getLispFilterInputStreamResult(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLispFilterInputStreamResult(String inputString) throws IOException {
|
private String getLispFilterInputStreamResult(String inputString) throws IOException {
|
||||||
InputStream stringInputStream = createInputStreamFromString(inputString);
|
InputStream stringInputStream = TestUtilities.createInputStreamFromString(inputString);
|
||||||
LispFilterInputStream lispFilterInputStream = new LispFilterInputStream(stringInputStream);
|
LispFilterInputStream lispFilterInputStream = new LispFilterInputStream(stringInputStream);
|
||||||
|
|
||||||
return readInputStreamIntoString(lispFilterInputStream);
|
return readInputStreamIntoString(lispFilterInputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream createInputStreamFromString(String string) {
|
|
||||||
return new ByteArrayInputStream(string.getBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readInputStreamIntoString(InputStream inputStream) throws IOException {
|
private String readInputStreamIntoString(InputStream inputStream) throws IOException {
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
int c = inputStream.read();
|
int c = inputStream.read();
|
||||||
|
|
||||||
while (c != -1) {
|
while (c != -1) {
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package scanner;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import scanner.Token.Type;
|
||||||
|
import testutils.TestUtilities;
|
||||||
|
|
||||||
|
public class LispScannerTester {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenEmptyFile_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "";
|
||||||
|
Token.Type[] expectedTypes = {};
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenNil_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "()";
|
||||||
|
Token.Type[] expectedTypes = { Type.LEFT_PAREN, Type.RIGHT_PAREN };
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenListOfNumbers_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "(1 2)";
|
||||||
|
Token.Type[] expectedTypes = { Type.LEFT_PAREN, Type.NUMBER, Type.NUMBER, Type.RIGHT_PAREN };
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenString_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "\"string\"";
|
||||||
|
Token.Type[] expectedTypes = { Type.STRING };
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenStringWithEscapedDoubleQuote_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "\"string \n hi \\\" bye\"";
|
||||||
|
Token.Type[] expectedTypes = { Type.STRING };
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenStringWithEscapedDoubleQuoteAndComment_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "\"string \n hi \\\" ; bye\"";
|
||||||
|
Token.Type[] expectedTypes = { Type.STRING };
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIdentifier_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "abcdefgHIJKLMNOP1234";
|
||||||
|
Token.Type[] expectedTypes = { Type.IDENTIFIER };
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenQuote_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "'";
|
||||||
|
Token.Type[] expectedTypes = { Type.QUOTE_MARK };
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenFunctionCall_returnsCorrectTokenTypes() throws IOException {
|
||||||
|
String input = "(defun myFunction (x)\n (print x))";
|
||||||
|
Token.Type[] expectedTypes = { Type.LEFT_PAREN, Type.IDENTIFIER, Type.IDENTIFIER, Type.LEFT_PAREN,
|
||||||
|
Type.IDENTIFIER, Type.RIGHT_PAREN, Type.LEFT_PAREN, Type.IDENTIFIER,
|
||||||
|
Type.IDENTIFIER, Type.RIGHT_PAREN, Type.RIGHT_PAREN };
|
||||||
|
|
||||||
|
assertTokenTypesMatch(input, expectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTokenTypesMatch(String input, Token.Type[] expectedTypeList) throws IOException {
|
||||||
|
InputStream stringInputStream = TestUtilities.createInputStreamFromString(input);
|
||||||
|
LispScanner lispScanner = new LispScanner(stringInputStream, "stringInputStream");
|
||||||
|
|
||||||
|
for (Token.Type expectedType : expectedTypeList)
|
||||||
|
assertEquals(expectedType, lispScanner.nextToken().getType());
|
||||||
|
|
||||||
|
assertEquals(Token.Type.EOF, lispScanner.nextToken().getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package testutils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class TestUtilities {
|
||||||
|
|
||||||
|
public static InputStream createInputStreamFromString(String string) {
|
||||||
|
return new ByteArrayInputStream(string.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue