Remove old lexer+parser implementation
This commit is contained in:
		
							parent
							
								
									d5632e9217
								
							
						
					
					
						commit
						a4ec29f084
					
				
					 15 changed files with 0 additions and 706 deletions
				
			
		|  | @ -1,127 +0,0 @@ | |||
| package lexer | ||||
| 
 | ||||
| import lexer.errors.LexingError | ||||
| import lexer.errors.LexingErrorType | ||||
| import lexer.state.LexerPosition | ||||
| import lexer.state.TokenPosition | ||||
| 
 | ||||
| class Lexer(private val source: String) { | ||||
|     private var tokens: List<Token> = emptyList() | ||||
|     private val position = LexerPosition(0, 0, -1) | ||||
| 
 | ||||
|     /** | ||||
|      * Scans the source code and returns a list of tokens. | ||||
|      * @return List of [Token]s | ||||
|      */ | ||||
|     fun scan(): List<Token> { | ||||
|         while (hasNext()) { | ||||
|             val char: Char = peek() | ||||
|             tokens += when { | ||||
|                 char == '(' -> scanSymbol(TokenType.PARENTHESIS_LEFT) | ||||
|                 char == ')' -> scanSymbol(TokenType.PARENTHESIS_RIGHT) | ||||
|                 char == '.' -> scanSymbol(TokenType.DOT) | ||||
|                 char == '"' -> scanQuotedString() | ||||
|                 char == '%' -> { scanComment(); continue } | ||||
|                 char.isLetterOrDigit() -> scanAlphanumeric() | ||||
|                 char.isWhitespace()    -> { scanWhitespace(); continue } | ||||
|                 else -> throw LexingError(LexingErrorType.UNKNOWN_TOKEN, "Did not recognize $char", position) | ||||
|             } | ||||
|         } | ||||
|         tokens += Token(TokenType.EOF, "EOF", getPosition(0)) | ||||
|         return tokens | ||||
|     } | ||||
| 
 | ||||
|     private fun hasNext(): Boolean { | ||||
|         // Check if the position is within the source length | ||||
|         return position.offset < source.length | ||||
|     } | ||||
| 
 | ||||
|     private fun peek(): Char { | ||||
|         // Peek should only be called if there is a next character | ||||
|         require(hasNext()) { | ||||
|             LexingError(LexingErrorType.UNEXPECTED_END_OF_INPUT, "Expected additional character", position) | ||||
|         } | ||||
| 
 | ||||
|         return source[position.offset] | ||||
|     } | ||||
| 
 | ||||
|     private fun next(): Char { | ||||
|         // Advance the position and return the character | ||||
|         val char = peek() | ||||
|         position.offset++ | ||||
|         position.column++ | ||||
|         return char | ||||
|     } | ||||
| 
 | ||||
|     private fun getPosition(length: Int = 1): TokenPosition { | ||||
|         // Return a new TokenPosition based on the current LexerPosition | ||||
|         return TokenPosition(position.line, position.column, length) | ||||
|     } | ||||
| 
 | ||||
|     /* * * * * * * | ||||
|      * Scanners  * | ||||
|      * * * * * * */ | ||||
| 
 | ||||
|     /** | ||||
|      * Scans a symbol token, given the expected [TokenType]. | ||||
|      * @param tokenType The expected [TokenType] | ||||
|      * @return The scanned [Token] | ||||
|      */ | ||||
|     private fun scanSymbol(tokenType: TokenType): Token { | ||||
|         return Token(tokenType, next().toString(), getPosition(1)) | ||||
|     } | ||||
| 
 | ||||
|     private fun scanAlphanumeric(): Token { | ||||
|         // Scan all alphanumeric characters | ||||
|         var length = 0 | ||||
|         while (hasNext() && peek().isLetterOrDigit()) { | ||||
|             next() | ||||
|             length++ | ||||
|         } | ||||
|         val value = source.substring(position.offset - length, position.offset) | ||||
|         return Token(TokenType.ALPHANUMERIC, value, getPosition(length)) | ||||
|     } | ||||
| 
 | ||||
|     private fun scanQuotedString(): Token { | ||||
|         // "Assert" that the next character is the start of a quoted string | ||||
|         require(next() == '"') { | ||||
|             LexingError(LexingErrorType.UNEXPECTED_TOKEN, "Expected opening quote '('", position) | ||||
|         } | ||||
| 
 | ||||
|         var length = 0 | ||||
|         while (hasNext() && peek() != '"') { | ||||
|             next() | ||||
|             length++ | ||||
|         } | ||||
| 
 | ||||
|         // "Assert" that the next character is the end of the quoted string | ||||
|         require(next() == '"') { | ||||
|             LexingError(LexingErrorType.UNEXPECTED_TOKEN, "Expected closing quote ')'", position) | ||||
|         } | ||||
| 
 | ||||
|         val value = source.substring(position.offset - length - 1, position.offset - 1) | ||||
|         return Token(TokenType.ALPHANUMERIC, value, getPosition(length)) | ||||
|     } | ||||
| 
 | ||||
|     private fun scanComment() { | ||||
|         // "Assert" that the next character is the start of a comment | ||||
|         require(next() == '%') { | ||||
|             LexingError(LexingErrorType.UNEXPECTED_TOKEN, "Expected opening comment '%'", position) | ||||
|         } | ||||
| 
 | ||||
|         // Skip all characters until the end of the line | ||||
|         while (hasNext() && peek() != '\n') { | ||||
|             next() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun scanWhitespace() { | ||||
|         // Skip all whitespace characters | ||||
|         while (hasNext() && peek().isWhitespace()) { | ||||
|             if (next() == '\n') { | ||||
|                 position.line++ | ||||
|                 position.column = 0 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,9 +0,0 @@ | |||
| package lexer | ||||
| 
 | ||||
| import lexer.state.TokenPosition | ||||
| 
 | ||||
| data class Token( | ||||
|     val type: TokenType, | ||||
|     val value: String, | ||||
|     val position: TokenPosition | ||||
| ) | ||||
|  | @ -1,15 +0,0 @@ | |||
| package lexer | ||||
| 
 | ||||
| enum class TokenType { | ||||
|     ALPHANUMERIC, | ||||
|     // TODO Replace with SMALL_LETTER, CAPITAL_LETTER, DIGIT, HEX_DIGIT, ... ? | ||||
| 
 | ||||
|     // Structure | ||||
|     COMMA, | ||||
|     DOT, | ||||
|     PARENTHESIS_LEFT, PARENTHESIS_RIGHT, | ||||
| 
 | ||||
|     // Special | ||||
| 
 | ||||
|     EOF | ||||
| } | ||||
|  | @ -1,13 +0,0 @@ | |||
| package lexer.errors | ||||
| 
 | ||||
| import lexer.state.LexerPosition | ||||
| 
 | ||||
| data class LexingError( | ||||
|     val type: LexingErrorType, | ||||
|     override val message: String, | ||||
|     val position: LexerPosition | ||||
| ) : Throwable( | ||||
|     """ | ||||
|     ${position.line}:${position.column + 1} ${type}: $message | ||||
| """.trimIndent() | ||||
| ) | ||||
|  | @ -1,7 +0,0 @@ | |||
| package lexer.errors | ||||
| 
 | ||||
| enum class LexingErrorType { | ||||
|     UNKNOWN_TOKEN, | ||||
|     UNEXPECTED_TOKEN, | ||||
|     UNEXPECTED_END_OF_INPUT, | ||||
| } | ||||
|  | @ -1,3 +0,0 @@ | |||
| package lexer.state | ||||
| 
 | ||||
| data class LexerPosition(var offset: Int, var line: Int, var column: Int) | ||||
|  | @ -1,3 +0,0 @@ | |||
| package lexer.state | ||||
| 
 | ||||
| data class TokenPosition(val line: Int, val column: Int, val length: Int) | ||||
|  | @ -1,137 +0,0 @@ | |||
| package parser | ||||
| 
 | ||||
| import lexer.Token | ||||
| import lexer.TokenType | ||||
| import parser.errors.ParsingError | ||||
| import parser.errors.ParsingErrorType | ||||
| import parser.state.ParserPosition | ||||
| import prolog.ast.logic.Clause | ||||
| import prolog.ast.logic.Fact | ||||
| import prolog.ast.logic.Rule | ||||
| import prolog.ast.terms.Atom | ||||
| import prolog.ast.terms.Structure | ||||
| import prolog.ast.terms.Term | ||||
| 
 | ||||
| class Parser(private val tokens: List<Token>) { | ||||
|     private val position: ParserPosition = ParserPosition(0) | ||||
| 
 | ||||
|     fun parse(): List<Term> { | ||||
|         val terms = mutableListOf<Term>() | ||||
| 
 | ||||
|         while (hasNext()) { | ||||
|             position.save() | ||||
| 
 | ||||
|             var term: Term? = null | ||||
| 
 | ||||
|             while (term == null) { | ||||
|                 // Try each parser rule in order | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|             require(term != null) { | ||||
|                 ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Expected a term", position) | ||||
|             } | ||||
| 
 | ||||
|             terms.add(term) | ||||
|         } | ||||
| 
 | ||||
|         return terms | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Matches the current token with any of the expected types. | ||||
|      * If it matches, it consumes the token and returns true. | ||||
|      * | ||||
|      * @param types The list of expected token types. | ||||
|      * @return True if the current token matches any of the expected types, false otherwise. | ||||
|      */ | ||||
|     private fun match(types: List<TokenType>): Boolean { | ||||
|         for (type in types) { | ||||
|             if (check(type)) { | ||||
|                 next() | ||||
|                 return true | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return false | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if the current token matches the expected type. | ||||
|      */ | ||||
|     private fun check(type: TokenType): Boolean { | ||||
|         return hasNext() && peek().type == type | ||||
|     } | ||||
| 
 | ||||
|     private fun hasNext(): Boolean { | ||||
|         // Check if the position is within the tokens list | ||||
|         // TODO Check for EOF instead? | ||||
|         return position.offset < tokens.size | ||||
|     } | ||||
| 
 | ||||
|     private fun peek(): Token { | ||||
|         require(hasNext()) { "Unexpected end of input" } | ||||
| 
 | ||||
|         return tokens[position.offset] | ||||
|     } | ||||
| 
 | ||||
|     private fun next(): Token { | ||||
|         val token = peek() | ||||
|         position.offset++ | ||||
|         return token | ||||
|     } | ||||
| 
 | ||||
|     private fun previous(): Token { | ||||
|         require(0 < position.offset) { "No previous token" } | ||||
|         return tokens[position.offset - 1] | ||||
|     } | ||||
| 
 | ||||
|     /* * * * * * | ||||
|      * Parsers * | ||||
|      * * * * * */ | ||||
| 
 | ||||
|     private fun parseWithTry(parseRule: () -> Term): Term { | ||||
|         try { | ||||
|             return parseRule() | ||||
|         } catch (e: Exception) { | ||||
|             throw ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Unexpected token", position) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun parseClause(): Clause { | ||||
|         return try { | ||||
|             Fact(parseStructure()) | ||||
|         } catch (e: Exception) { | ||||
|             Fact(parseAtom()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun parseStructure(): Structure { | ||||
|         val name = parseAtom() | ||||
|         val args = mutableListOf<Term>() | ||||
| 
 | ||||
|         require(match(listOf(TokenType.PARENTHESIS_LEFT))) { | ||||
|             ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Expected '(' after structure name", position) | ||||
|         } | ||||
| 
 | ||||
|         // TODO Handle arguments | ||||
| 
 | ||||
|         require(match(listOf(TokenType.PARENTHESIS_RIGHT))) { | ||||
|             ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Expected ')' after structure arguments", position) | ||||
|         } | ||||
| 
 | ||||
|         return Structure(name, args) | ||||
|     } | ||||
| 
 | ||||
|     private fun parseAtom(): Atom { | ||||
|         return Atom(parseLetterDigit()) | ||||
|     } | ||||
| 
 | ||||
|     private fun parseLetterDigit(): String { | ||||
|         require(match(listOf(TokenType.ALPHANUMERIC)) && previous().value[0].isLowerCase()) { | ||||
|             ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Expected lowercase letter", position) | ||||
|         } | ||||
| 
 | ||||
|         return previous().value | ||||
|     } | ||||
| } | ||||
|  | @ -1,12 +0,0 @@ | |||
| package parser.errors | ||||
| 
 | ||||
| import parser.state.ParserPosition | ||||
| 
 | ||||
| class ParsingError(private val type: ParsingErrorType, override val message: String, private val position: ParserPosition) : | ||||
|     Throwable() { | ||||
|     override fun toString(): String { | ||||
|         return """ | ||||
|             ($position) ${type}: $message | ||||
|         """.trimIndent() | ||||
|     } | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| package parser.errors | ||||
| 
 | ||||
| enum class ParsingErrorType { | ||||
|     UNEXPECTED_TOKEN, | ||||
| 
 | ||||
|     INTERNAL_ERROR, | ||||
| } | ||||
|  | @ -1,25 +0,0 @@ | |||
| package parser.state | ||||
| 
 | ||||
| import parser.errors.ParsingError | ||||
| import parser.errors.ParsingErrorType | ||||
| 
 | ||||
| data class ParserPosition(var offset: Int) { | ||||
|     private val checkpoints: ArrayDeque<ParserPosition> = ArrayDeque() | ||||
| 
 | ||||
|     fun save() { | ||||
|         checkpoints.addLast(this.copy()) | ||||
|     } | ||||
| 
 | ||||
|     fun reload() { | ||||
|         require(checkpoints.isNotEmpty()) { | ||||
|             ParsingError(ParsingErrorType.INTERNAL_ERROR, "No checkpoint to reload from", this) | ||||
|         } | ||||
| 
 | ||||
|         val checkpoint = checkpoints.removeLast() | ||||
|         offset = checkpoint.offset | ||||
|     } | ||||
| 
 | ||||
|     override fun toString(): String { | ||||
|         return "at $offset" | ||||
|     } | ||||
| } | ||||
|  | @ -1,62 +0,0 @@ | |||
| package lexer | ||||
| 
 | ||||
| import lexer.errors.LexingError | ||||
| import org.junit.jupiter.api.Test | ||||
| import org.junit.jupiter.api.assertThrows | ||||
| import kotlin.test.assertEquals | ||||
| 
 | ||||
| /** | ||||
|  * Tests for the Prolog lexer. | ||||
|  * | ||||
|  * These tests are based on the Prolog syntax. | ||||
|  */ | ||||
| class ScanPrologParserTests { | ||||
|     @Test | ||||
|     fun scan_simple_atom() { | ||||
|         val tokens = Lexer("atom.").scan() | ||||
| 
 | ||||
|         assertEquals(3, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.DOT, tokens[1].type, "Expected DOT token, got ${tokens[1].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[2].type, "Expected EOF token, got ${tokens[2].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_variable() { | ||||
|         val tokens = Lexer("X.").scan() | ||||
| 
 | ||||
|         assertEquals(3, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.DOT, tokens[1].type, "Expected DOT token, got ${tokens[1].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[2].type, "Expected EOF token, got ${tokens[2].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_variable_with_number() { | ||||
|         val tokens = Lexer("X1.").scan() | ||||
| 
 | ||||
|         assertEquals(3, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.DOT, tokens[1].type, "Expected DOT token, got ${tokens[1].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[2].type, "Expected EOF token, got ${tokens[2].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_variable_with_underscore() { | ||||
|         val tokens = Lexer("X_1.").scan() | ||||
| 
 | ||||
|         assertEquals(3, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.DOT, tokens[1].type, "Expected DOT token, got ${tokens[1].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[2].type, "Expected EOF token, got ${tokens[2].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_variable_that_starts_with_a_number() { | ||||
|         assertThrows<LexingError> { Lexer("1X.").scan() } | ||||
|     } | ||||
| } | ||||
|  | @ -1,191 +0,0 @@ | |||
| package lexer | ||||
| 
 | ||||
| import lexer.errors.LexingError | ||||
| import org.junit.jupiter.api.Test | ||||
| import org.junit.jupiter.api.assertThrows | ||||
| import org.junit.jupiter.api.Assertions.* | ||||
| 
 | ||||
| class ScanTests { | ||||
|     @Test | ||||
|     fun scan_emptyString_returns_EOF() { | ||||
|         val tokens = Lexer("").scan() | ||||
|         assertEquals(1, tokens.size, "Expected 1 token, got ${tokens.size}") | ||||
|         assertEquals(TokenType.EOF, tokens[0].type, "Expected EOF token, got ${tokens[0].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_unknownSymbol_returns_Error() { | ||||
|         assertThrows<LexingError> { Lexer("€").scan() } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_dot_returns_Dot() { | ||||
|         val tokens = Lexer(".").scan() | ||||
|         assertEquals(2, tokens.size) | ||||
|         assertEquals(TokenType.DOT, tokens[0].type, "Expected DOT token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_two_dots_returns_two_dots() { | ||||
|         val tokens = Lexer("..").scan() | ||||
|         assertEquals(3, tokens.size) | ||||
|         assertEquals(TokenType.DOT, tokens[0].type, "Expected DOT token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.DOT, tokens[1].type, "Expected DOT token, got ${tokens[1].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[2].type, "Expected EOF token, got ${tokens[2].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_letter_returns_letter() { | ||||
|         val tokens = Lexer("a").scan() | ||||
| 
 | ||||
|         assertEquals(2, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}") | ||||
| 
 | ||||
|         assertEquals(0, tokens[0].position.line, "Expected line 0, got ${tokens[0].position.line}") | ||||
|         assertEquals(0, tokens[0].position.column, "Expected column 0, got ${tokens[0].position.column}") | ||||
|         assertEquals(1, tokens[0].position.length, "Expected length 1, got ${tokens[0].position.length}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_word_returns_alphanumerics() { | ||||
|         val lexer = Lexer("word") | ||||
|         val tokens = lexer.scan() | ||||
| 
 | ||||
|         assertEquals(2, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}") | ||||
| 
 | ||||
|         assertEquals(4, tokens[0].position.length, "Expected length 4, got ${tokens[0].position.length}") | ||||
| 
 | ||||
|         assertEquals("word", tokens[0].value, "Expected 'word', got ${tokens[0].value}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_space_returns_nothing() { | ||||
|         val lexer = Lexer(" ") | ||||
|         val tokens = lexer.scan() | ||||
| 
 | ||||
|         assertEquals(1, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.EOF, tokens[0].type, "Expected EOF token, got ${tokens[0].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_whitespace_various_returns_nothing() { | ||||
|         val lexer = Lexer(" \t\n\r") | ||||
|         val tokens = lexer.scan() | ||||
| 
 | ||||
|         assertEquals(1, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.EOF, tokens[0].type, "Expected EOF token, got ${tokens[0].type}") | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_separated_words() { | ||||
|         val tokens = Lexer("word1 word2").scan() | ||||
| 
 | ||||
|         assertEquals(3, tokens.size, "Expected 3 tokens, got ${tokens.size}") | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals("word1", tokens[0].value, "Expected 'word1', got ${tokens[0].value}") | ||||
|         assertEquals(5, tokens[0].position.length, "Expected length 5, got ${tokens[0].position.length}") | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[1].type, "Expected ALPHANUMERIC token, got ${tokens[1].type}") | ||||
|         assertEquals("word2", tokens[1].value, "Expected 'word2', got ${tokens[1].value}") | ||||
|         assertEquals(5, tokens[1].position.length, "Expected length 5, got ${tokens[1].position.length}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_multiline() { | ||||
|         val tokens = Lexer( | ||||
|             """ | ||||
|             word1 | ||||
|             word2 | ||||
|         """.trimIndent() | ||||
|         ).scan() | ||||
| 
 | ||||
|         assertEquals(3, tokens.size, "Expected 3 tokens, got ${tokens.size}") | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals("word1", tokens[0].value, "Expected 'word1', got ${tokens[0].value}") | ||||
|         assertEquals(5, tokens[0].position.length, "Expected length 5, got ${tokens[0].position.length}") | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[1].type, "Expected ALPHANUMERIC token, got ${tokens[1].type}") | ||||
|         assertEquals("word2", tokens[1].value, "Expected 'word2', got ${tokens[1].value}") | ||||
|         assertEquals(5, tokens[1].position.length, "Expected length 5, got ${tokens[1].position.length}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_parenthesis_returns_parenthesis() { | ||||
|         val lexer = Lexer("()") | ||||
|         val tokens = lexer.scan() | ||||
| 
 | ||||
|         assertEquals(3, tokens.size) | ||||
| 
 | ||||
|         assertEquals( | ||||
|             TokenType.PARENTHESIS_LEFT, | ||||
|             tokens[0].type, | ||||
|             "Expected LEFT_PARENTHESES token, got ${tokens[0].type}" | ||||
|         ) | ||||
|         assertEquals( | ||||
|             TokenType.PARENTHESIS_RIGHT, | ||||
|             tokens[1].type, | ||||
|             "Expected RIGHT_PARENTHESES token, got ${tokens[1].type}" | ||||
|         ) | ||||
|         assertEquals(TokenType.EOF, tokens[2].type, "Expected EOF token, got ${tokens[2].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_simple_quoted_string_returns_string() { | ||||
|         val lexer = Lexer("\"string\"") | ||||
|         val tokens = lexer.scan() | ||||
| 
 | ||||
|         assertEquals(2, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}") | ||||
| 
 | ||||
|         assertEquals("string", tokens[0].value, "Expected 'string', got ${tokens[0].value}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_quoted_string_with_space_returns_string() { | ||||
|         val lexer = Lexer("\"string with space\"") | ||||
|         val tokens = lexer.scan() | ||||
| 
 | ||||
|         assertEquals(2, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals(TokenType.EOF, tokens[1].type, "Expected EOF token, got ${tokens[1].type}") | ||||
| 
 | ||||
|         assertEquals("string with space", tokens[0].value, "Expected 'string with space', got ${tokens[0].value}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_comments_returns_nothing() { | ||||
|         val lexer = Lexer("% comment") | ||||
|         val tokens = lexer.scan() | ||||
| 
 | ||||
|         assertEquals(1, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.EOF, tokens[0].type, "Expected EOF token, got ${tokens[0].type}") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun scan_comment_and_sentence_returns_sentence() { | ||||
|         val tokens = Lexer(""" | ||||
|             % comment | ||||
|             sentence | ||||
|         """.trimIndent()).scan() | ||||
| 
 | ||||
|         assertEquals(2, tokens.size) | ||||
| 
 | ||||
|         assertEquals(TokenType.ALPHANUMERIC, tokens[0].type, "Expected ALPHANUMERIC token, got ${tokens[0].type}") | ||||
|         assertEquals("sentence", tokens[0].value, "Expected 'sentence', got ${tokens[0].value}") | ||||
|     } | ||||
| } | ||||
|  | @ -1,4 +0,0 @@ | |||
| package parser | ||||
| 
 | ||||
| class ParseFromTextTests { | ||||
| } | ||||
|  | @ -1,91 +0,0 @@ | |||
| package parser | ||||
| 
 | ||||
| import lexer.Token | ||||
| import lexer.state.TokenPosition | ||||
| import lexer.TokenType | ||||
| import org.junit.jupiter.api.Assertions.assertEquals | ||||
| import org.junit.jupiter.api.Assertions.assertTrue | ||||
| import org.junit.jupiter.api.Test | ||||
| import prolog.ast.terms.Atom | ||||
| import prolog.ast.terms.CompoundTerm | ||||
| 
 | ||||
| class ParseTests { | ||||
|     @Test | ||||
|     fun `parse atom a`() { | ||||
|         val input = Token(TokenType.ALPHANUMERIC, "a", TokenPosition(0, 0, 1)) | ||||
| 
 | ||||
|         val result = Parser(listOf(input)).parse() | ||||
| 
 | ||||
|         assertEquals(1, result.size, "Expected 1 term") | ||||
|         assertEquals(Atom("a"), result[0], "Expected atom 'a'") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `parse atom foo`() { | ||||
|         val input = Token(TokenType.ALPHANUMERIC, "foo", TokenPosition(0, 0, 3)) | ||||
| 
 | ||||
|         val result = Parser(listOf(input)).parse() | ||||
| 
 | ||||
|         assertEquals(1, result.size, "Expected 1 term") | ||||
|         assertEquals(Atom("foo"), result[0], "Expected atom 'foo'") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `parse atom foo1`() { | ||||
|         val input = Token(TokenType.ALPHANUMERIC, "foo1", TokenPosition(0, 0, 4)) | ||||
| 
 | ||||
|         val result = Parser(listOf(input)).parse() | ||||
| 
 | ||||
|         assertEquals(1, result.size, "Expected 1 term") | ||||
|         assertEquals(Atom("foo1"), result[0], "Expected atom 'foo1'") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `parse atom fooBar`() { | ||||
|         val name = "fooBar" | ||||
|         val input = Token(TokenType.ALPHANUMERIC, name, TokenPosition(0, 0, 6)) | ||||
| 
 | ||||
|         val result = Parser(listOf(input)).parse() | ||||
| 
 | ||||
|         assertEquals(1, result.size, "Expected 1 term") | ||||
|         assertEquals(Atom(name), result[0], "Expected atom 'fooBar'") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `parse atom foo_bar`() { | ||||
|         val name = "foo_bar" | ||||
|         val input = Token(TokenType.ALPHANUMERIC, name, TokenPosition(0, 0, 7)) | ||||
| 
 | ||||
|         val result = Parser(listOf(input)).parse() | ||||
| 
 | ||||
|         assertEquals(1, result.size, "Expected 1 term") | ||||
|         assertEquals(Atom(name), result[0], "Expected atom 'foo_bar'") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `parse atom my_FooBar1`() { | ||||
|         val name = "my_FooBar1" | ||||
|         val input = Token(TokenType.ALPHANUMERIC, name, TokenPosition(0, 0, 11)) | ||||
| 
 | ||||
|         val result = Parser(listOf(input)).parse() | ||||
| 
 | ||||
|         assertEquals(1, result.size, "Expected 1 term") | ||||
|         assertEquals(Atom(name), result[0], "Expected atom 'my_FooBar1'") | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun `parse compound term f()`() { | ||||
|         val input = listOf( | ||||
|             Token(TokenType.ALPHANUMERIC, "f", TokenPosition(0, 0, 1)), | ||||
|             Token(TokenType.PARENTHESIS_LEFT, "(", TokenPosition(0, 1, 2)), | ||||
|             Token(TokenType.PARENTHESIS_RIGHT, ")", TokenPosition(0, 3, 4)) | ||||
|         ) | ||||
| 
 | ||||
|         val result = Parser(input).parse() | ||||
| 
 | ||||
|         assertEquals(1, result.size, "Expected 1 term") | ||||
|         assertTrue(result[0] is CompoundTerm) | ||||
|         assertEquals("f", (result[0] as CompoundTerm).name) | ||||
|         assertEquals(0, (result[0] as CompoundTerm).arguments.size) | ||||
|     } | ||||
| } | ||||
		Reference in a new issue