From 60f59e03d421a3f127bf8f19c80fb18d62245571 Mon Sep 17 00:00:00 2001 From: Lars Simon Winzer Date: Sun, 15 Mar 2026 13:26:59 +0100 Subject: [PATCH] Add: TokenClassifier to perform more extensive syntax verification and classify tokens --- .../server/tokenizer/TokenClassifier.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/tokenizer/TokenClassifier.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/tokenizer/TokenClassifier.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/tokenizer/TokenClassifier.java new file mode 100644 index 0000000..cb19021 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/tokenizer/TokenClassifier.java @@ -0,0 +1,103 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.tokenizer; + +import java.util.ArrayList; +import java.util.List; + +public class TokenClassifier { + public static List classify(List rawTokens) { + validateFirstToken(rawTokens); + + List tokens = new ArrayList<>(); + + for (int i = 0; i < rawTokens.size(); i++) { + RawToken raw = rawTokens.get(i); + + switch (raw.type()) { + case SEPARATOR -> { + validateSeparator(rawTokens, i); + tokens.add( + new Token(TokenType.SEPARATOR, raw.value(), raw.line(), raw.column())); + } + case WORD -> { + TokenType type = resolveWordType(rawTokens, i); + tokens.add(new Token(type, raw.value(), raw.line(), raw.column())); + } + case STRING -> { + validateString(rawTokens, i); + tokens.add(new Token(TokenType.VALUE, raw.value(), raw.line(), raw.column())); + } + + case NEWLINE -> { + throw new TokenizerException( + "Unexpected newline in request", raw.line(), raw.column()); + } + + case EOF -> { + tokens.add(new Token(TokenType.EOF, raw.value(), raw.line(), raw.column())); + } + } + } + + return tokens; + } + + private static void validateFirstToken(List rawTokens) { + if (rawTokens.isEmpty() || rawTokens.get(0).type() != RawTokenType.WORD) { + throw new TokenizerException("Expected COMMAND as first token", 1, 1); + } + } + + private static void validateSeparator(List rawTokens, int index) { + boolean missingKey = index == 0 || rawTokens.get(index - 1).type() != RawTokenType.WORD; + + boolean missingValue = + index + 1 >= rawTokens.size() + || (rawTokens.get(index + 1).type() != RawTokenType.WORD + && rawTokens.get(index + 1).type() != RawTokenType.STRING); + + boolean nextWordIsKey = + !missingValue + && rawTokens.get(index + 1).type() == RawTokenType.WORD + && index + 2 < rawTokens.size() + && rawTokens.get(index + 2).type() == RawTokenType.SEPARATOR; + + RawToken separator = rawTokens.get(index); + + if (missingKey) { + throw new TokenizerException( + "Expected KEY before '='", separator.line(), separator.column()); + } + + if (missingValue || nextWordIsKey) { + throw new TokenizerException( + "Expected VALUE after '='", separator.line(), separator.column()); + } + } + + private static void validateString(List rawTokens, int index) { + boolean afterSeparator = + index > 0 && rawTokens.get(index - 1).type() == RawTokenType.SEPARATOR; + + if (!afterSeparator) { + RawToken token = rawTokens.get(index); + throw new TokenizerException("Unexpected string literal", token.line(), token.column()); + } + } + + private static TokenType resolveWordType(List rawTokens, int index) { + boolean isFirst = index == 0; + boolean afterNewline = index > 0 && rawTokens.get(index - 1).type() == RawTokenType.NEWLINE; + boolean afterSeparator = + index > 0 && rawTokens.get(index - 1).type() == RawTokenType.SEPARATOR; + + if (isFirst || afterNewline) { + return TokenType.COMMAND; + } + + if (afterSeparator) { + return TokenType.VALUE; + } + + return TokenType.KEY; + } +}