Skip to content

Commit 803d5f8

Browse files
committed
week5 kodutöö lahendus
1 parent c39f2b2 commit 803d5f8

1 file changed

Lines changed: 204 additions & 0 deletions

File tree

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package week5;
2+
3+
import java.io.IOException;
4+
import java.io.Reader;
5+
import java.io.UncheckedIOException;
6+
import java.nio.charset.StandardCharsets;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.Paths;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
import static week5.AktkToken.Type.*;
14+
15+
public class AktkHandwrittenLexer {
16+
17+
// Siin on meie poolt mall ette antud, millega saad kasutada praktikumist tuttavad meetodid.
18+
// Selle malli kasutamine ei ole kohustuslik, aga sisend antakse ette Reader liidese kaudu
19+
// ja viimase testi läbimiseks ei tohi sisendis liiga palju ette vaadata.
20+
21+
private static final char TERMINATOR = '\0';
22+
private final Reader reader;
23+
private Character current;
24+
private int pos = 0;
25+
26+
public AktkHandwrittenLexer(Reader input) {
27+
this.reader = input;
28+
}
29+
30+
// See teeb meil testimise mugavamaks:
31+
public List<AktkToken> readAllTokens() {
32+
List<AktkToken> tokens = new ArrayList<>();
33+
34+
AktkToken token;
35+
do {
36+
token = this.readNextToken();
37+
tokens.add(token);
38+
} while (token.type() != EOF);
39+
40+
return tokens;
41+
}
42+
43+
// Saame kasutada näiteks järgmiselt:
44+
static void main() throws IOException {
45+
Path path = Paths.get("inputs", "escaped.txt");
46+
System.out.println(Files.readString(path));
47+
System.out.println(new AktkHandwrittenLexer(Files.newBufferedReader(path, StandardCharsets.UTF_8)).readAllTokens());
48+
}
49+
50+
51+
// Siin on abimeetodid, mida soovitame kasutada.
52+
// Consume on sisuliselt nagu pos++ kalalekseri näites ehk läheb järgmise tähe juurde.
53+
private void consume() {
54+
if (current == TERMINATOR) {
55+
throw new RuntimeException("Reading passed terminator!");
56+
}
57+
read();
58+
pos++;
59+
}
60+
61+
// Peek on see, mis tagastab hetkel vaadeldavat tähte.
62+
// (Kõige algul võib see täht olla puudu.)
63+
private char peek() {
64+
if (current == null) read();
65+
return current;
66+
}
67+
68+
// See on pigem abimeetod, mis teostab tegeliku sisendist lugemist.
69+
// Meie enda lahenduses me seda otse ei kasuta. Kutsume ainult peek ja consume.
70+
private void read() {
71+
try {
72+
int i = reader.read();
73+
current = (i == -1) ? TERMINATOR : (char) i;
74+
} catch (IOException e) {
75+
throw new UncheckedIOException(e);
76+
}
77+
}
78+
79+
80+
/**
81+
* See on nüüd see oluline meetod, mida peab ise implementeerima!
82+
*/
83+
public AktkToken readNextToken() {
84+
while (peek() != TERMINATOR) {
85+
if (Character.isDigit(peek())) {
86+
return readInteger();
87+
} else if (Character.isWhitespace(peek())) {
88+
consume();
89+
} else if (peek() == '"') {
90+
return readString();
91+
} else if (Character.isLetter(peek()) || peek() == '_') {
92+
return readVariableOrKeyword();
93+
} else if (peek() == '(') {
94+
consume();
95+
return singleCharToken(LPAREN);
96+
} else if (peek() == ')') {
97+
consume();
98+
return singleCharToken(RPAREN);
99+
} else if (peek() == '+') {
100+
consume();
101+
return singleCharToken(PLUS);
102+
} else if (peek() == '-') {
103+
consume();
104+
return singleCharToken(MINUS);
105+
} else if (peek() == '*') {
106+
consume();
107+
return singleCharToken(TIMES);
108+
} else if (peek() == '/') {
109+
consume();
110+
if (peek() == '/') {
111+
consume();
112+
skipSingleLineComment();
113+
} else if (peek() == '*') {
114+
consume();
115+
skipMultiLineComment();
116+
} else {
117+
return singleCharToken(DIV);
118+
}
119+
} else {
120+
throw new RuntimeException("Unexpected symbol");
121+
}
122+
}
123+
return new AktkToken(EOF, pos, 0);
124+
}
125+
126+
private AktkToken singleCharToken(AktkToken.Type type) {
127+
return new AktkToken(type, pos - 1, 1);
128+
}
129+
130+
private void skipMultiLineComment() {
131+
do {
132+
while (peek() != '*') consume();
133+
consume();
134+
} while (peek() != '/');
135+
consume();
136+
}
137+
138+
private void skipSingleLineComment() {
139+
while (peek() != '\n' && peek() != TERMINATOR) consume();
140+
}
141+
142+
private AktkToken readVariableOrKeyword() {
143+
assert Character.isLetter(peek()) || peek() == '_';
144+
int initPos = this.pos;
145+
146+
StringBuilder sb = new StringBuilder();
147+
sb.append(peek());
148+
consume();
149+
150+
while ((Character.isLetter(peek())
151+
|| Character.isDigit(peek())
152+
|| peek() == '_')) {
153+
sb.append(peek());
154+
consume();
155+
}
156+
157+
String content = sb.toString();
158+
return switch (content) {
159+
case "if" -> new AktkToken(IF, initPos, content.length());
160+
case "while" -> new AktkToken(WHILE, initPos, content.length());
161+
case "var" -> new AktkToken(VAR, initPos, content.length());
162+
default -> new AktkToken(VARIABLE, content, initPos, content.length());
163+
};
164+
}
165+
166+
private AktkToken readString() {
167+
assert peek() == '"';
168+
int initPos = this.pos;
169+
170+
StringBuilder sb = new StringBuilder();
171+
172+
consume();
173+
while (peek() != '"') {
174+
if (peek() == '\\') {
175+
consume();
176+
switch (peek()) {
177+
case 'n' -> sb.append('\n');
178+
case 't' -> sb.append('\t');
179+
case '"' -> sb.append('\"');
180+
default -> throw new RuntimeException("Unknown character escape");
181+
}
182+
} else {
183+
sb.append(peek());
184+
}
185+
consume();
186+
}
187+
consume();
188+
189+
return new AktkToken(STRING, sb.toString(), initPos, this.pos - initPos);
190+
}
191+
192+
private AktkToken readInteger() {
193+
assert Character.isDigit(peek());
194+
195+
int initPos = this.pos;
196+
int result = 0;
197+
while (Character.isDigit(peek())) {
198+
result = 10 * result + Character.getNumericValue(peek());
199+
consume();
200+
}
201+
202+
return new AktkToken(INTEGER, result, initPos, pos - initPos);
203+
}
204+
}

0 commit comments

Comments
 (0)