mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
More informative parser errors with ranges, removed LexerException
This commit is contained in:
parent
923f0edbcb
commit
ea38227b44
@ -0,0 +1,20 @@
|
||||
package com.annimon.ownlang.exceptions;
|
||||
|
||||
import com.annimon.ownlang.parser.Range;
|
||||
|
||||
/**
|
||||
* Base type for all lexer and parser exceptions
|
||||
*/
|
||||
public abstract class BaseParserException extends RuntimeException {
|
||||
|
||||
private final Range range;
|
||||
|
||||
public BaseParserException(String message, Range range) {
|
||||
super(message);
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
public Range getRange() {
|
||||
return range;
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package com.annimon.ownlang.exceptions;
|
||||
|
||||
import com.annimon.ownlang.parser.Pos;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public final class LexerException extends OwnLangParserException {
|
||||
|
||||
public LexerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LexerException(String message, Pos pos) {
|
||||
super(pos.format() + " " + message);
|
||||
}
|
||||
}
|
@ -1,15 +1,27 @@
|
||||
package com.annimon.ownlang.exceptions;
|
||||
|
||||
/**
|
||||
* Base type for all lexer and parser exceptions
|
||||
*/
|
||||
public abstract class OwnLangParserException extends RuntimeException {
|
||||
import com.annimon.ownlang.parser.error.ParseError;
|
||||
import com.annimon.ownlang.parser.error.ParseErrors;
|
||||
|
||||
public OwnLangParserException() {
|
||||
super();
|
||||
/**
|
||||
* Single Exception for Lexer and Parser errors
|
||||
*/
|
||||
public class OwnLangParserException extends RuntimeException {
|
||||
|
||||
private final ParseErrors parseErrors;
|
||||
|
||||
public OwnLangParserException(ParseError parseError) {
|
||||
super(parseError.toString());
|
||||
this.parseErrors = new ParseErrors();
|
||||
parseErrors.add(parseError);;
|
||||
}
|
||||
|
||||
public OwnLangParserException(String message) {
|
||||
super(message);
|
||||
public OwnLangParserException(ParseErrors parseErrors) {
|
||||
super(parseErrors.toString());
|
||||
this.parseErrors = parseErrors;
|
||||
}
|
||||
|
||||
public ParseErrors getParseErrors() {
|
||||
return parseErrors;
|
||||
}
|
||||
}
|
@ -1,34 +1,18 @@
|
||||
package com.annimon.ownlang.exceptions;
|
||||
|
||||
import com.annimon.ownlang.parser.Pos;
|
||||
import com.annimon.ownlang.parser.Range;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public final class ParseException extends OwnLangParserException {
|
||||
|
||||
private final Range range;
|
||||
public final class ParseException extends BaseParserException {
|
||||
|
||||
public ParseException(String message) {
|
||||
this(message, Range.ZERO);
|
||||
}
|
||||
|
||||
public ParseException(String message, Pos pos) {
|
||||
this(message, pos, pos);
|
||||
}
|
||||
|
||||
public ParseException(String message, Pos start, Pos end) {
|
||||
this(message, new Range(start, end));
|
||||
super(message, Range.ZERO);
|
||||
}
|
||||
|
||||
public ParseException(String message, Range range) {
|
||||
super(message);
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
public Range getRange() {
|
||||
return range;
|
||||
super(message, range);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.annimon.ownlang.parser;
|
||||
|
||||
import com.annimon.ownlang.exceptions.LexerException;
|
||||
import com.annimon.ownlang.exceptions.OwnLangParserException;
|
||||
import com.annimon.ownlang.parser.error.ParseError;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@ -146,7 +147,7 @@ public final class Lexer {
|
||||
else if (current == '#') tokenizeHexNumber(1);
|
||||
else if (current == ';') skip(); // ignore semicolon
|
||||
else if (current == '\0') break;
|
||||
else throw error("Unknown token " + current);
|
||||
else throw error("Unknown token " + current, markPos());
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
@ -164,11 +165,11 @@ public final class Lexer {
|
||||
while (true) {
|
||||
if (current == '.') {
|
||||
decimal = true;
|
||||
if (hasDot) throw error("Invalid float number " + buffer);
|
||||
if (hasDot) throw error("Invalid float number " + buffer, startPos);
|
||||
hasDot = true;
|
||||
} else if (current == 'e' || current == 'E') {
|
||||
decimal = true;
|
||||
int exp = subTokenizeScientificNumber();
|
||||
int exp = subTokenizeScientificNumber(startPos);
|
||||
buffer.append(current).append(exp);
|
||||
break;
|
||||
} else if (!Character.isDigit(current)) {
|
||||
@ -184,7 +185,7 @@ public final class Lexer {
|
||||
}
|
||||
}
|
||||
|
||||
private int subTokenizeScientificNumber() {
|
||||
private int subTokenizeScientificNumber(Pos startPos) {
|
||||
int sign = 1;
|
||||
switch (next()) {
|
||||
case '-': sign = -1;
|
||||
@ -204,10 +205,10 @@ public final class Lexer {
|
||||
current = next();
|
||||
position++;
|
||||
}
|
||||
if (position == 0 && !hasValue) throw error("Empty floating point exponent");
|
||||
if (position == 0 && !hasValue) throw error("Empty floating point exponent", startPos, markEndPos());
|
||||
if (position >= 4) {
|
||||
if (sign > 0) throw error("Float number too large");
|
||||
else throw error("Float number too small");
|
||||
if (sign > 0) throw error("Float number too large", startPos, markEndPos());
|
||||
else throw error("Float number too small", startPos, markEndPos());
|
||||
}
|
||||
return sign * result;
|
||||
}
|
||||
@ -227,8 +228,8 @@ public final class Lexer {
|
||||
current = next();
|
||||
}
|
||||
|
||||
if (buffer.isEmpty()) throw error("Empty HEX value");
|
||||
if (peek(-1) == '_') throw error("HEX value cannot end with _");
|
||||
if (buffer.isEmpty()) throw error("Empty HEX value", startPos);
|
||||
if (peek(-1) == '_') throw error("HEX value cannot end with _", startPos, markEndPos());
|
||||
addToken(TokenType.HEX_NUMBER, buffer.toString(), startPos);
|
||||
}
|
||||
|
||||
@ -290,8 +291,9 @@ public final class Lexer {
|
||||
final var buffer = createBuffer();
|
||||
char current = peek(0);
|
||||
while (current != '`') {
|
||||
if (current == '\0') throw error("Reached end of file while parsing extended word.");
|
||||
if (current == '\n' || current == '\r') throw error("Reached end of line while parsing extended word.");
|
||||
if ("\r\n\0".indexOf(current) != -1) {
|
||||
throw error("Reached end of line while parsing extended word.", startPos, markEndPos());
|
||||
}
|
||||
buffer.append(current);
|
||||
current = next();
|
||||
}
|
||||
@ -341,7 +343,7 @@ public final class Lexer {
|
||||
continue;
|
||||
}
|
||||
if (current == '"') break;
|
||||
if (current == '\0') throw error("Reached end of file while parsing text string.");
|
||||
if (current == '\0') throw error("Reached end of file while parsing text string.", startPos, markEndPos());
|
||||
buffer.append(current);
|
||||
current = next();
|
||||
}
|
||||
@ -360,11 +362,14 @@ public final class Lexer {
|
||||
}
|
||||
|
||||
private void tokenizeMultilineComment() {
|
||||
final Pos startPos = markPos();
|
||||
skip(); // /
|
||||
skip(); // *
|
||||
char current = peek(0);
|
||||
while (current != '*' || peek(1) != '/') {
|
||||
if (current == '\0') throw error("Reached end of file while parsing multiline comment");
|
||||
if (current == '\0') {
|
||||
throw error("Reached end of file while parsing multiline comment", startPos, markEndPos());
|
||||
}
|
||||
current = next();
|
||||
}
|
||||
skip(); // *
|
||||
@ -388,6 +393,10 @@ public final class Lexer {
|
||||
return new Pos(row, col);
|
||||
}
|
||||
|
||||
private Pos markEndPos() {
|
||||
return new Pos(row, Math.max(0, col - 1));
|
||||
}
|
||||
|
||||
private void skip() {
|
||||
if (pos >= length) return;
|
||||
final char result = input.charAt(pos);
|
||||
@ -417,7 +426,11 @@ public final class Lexer {
|
||||
tokens.add(new Token(type, text, startRow));
|
||||
}
|
||||
|
||||
private LexerException error(String text) {
|
||||
return new LexerException(text, markPos());
|
||||
private OwnLangParserException error(String text, Pos position) {
|
||||
return error(text, position, position);
|
||||
}
|
||||
|
||||
private OwnLangParserException error(String text, Pos startPos, Pos endPos) {
|
||||
return new OwnLangParserException(new ParseError(text, new Range(startPos, endPos)));
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package com.annimon.ownlang.parser;
|
||||
|
||||
import com.annimon.ownlang.exceptions.OwnLangParserException;
|
||||
import com.annimon.ownlang.exceptions.ParseException;
|
||||
import com.annimon.ownlang.lib.NumberValue;
|
||||
import com.annimon.ownlang.lib.StringValue;
|
||||
import com.annimon.ownlang.lib.UserDefinedFunction;
|
||||
import com.annimon.ownlang.parser.ast.*;
|
||||
import com.annimon.ownlang.parser.error.ParseError;
|
||||
import com.annimon.ownlang.parser.error.ParseErrors;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@ -19,7 +22,7 @@ public final class Parser {
|
||||
final Parser parser = new Parser(tokens);
|
||||
final Statement program = parser.parse();
|
||||
if (parser.getParseErrors().hasErrors()) {
|
||||
throw new ParseException(parser.getParseErrors().toString());
|
||||
throw new OwnLangParserException(parser.getParseErrors());
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.annimon.ownlang.parser.ast;
|
||||
|
||||
import com.annimon.ownlang.exceptions.ParseException;
|
||||
import com.annimon.ownlang.parser.Lexer;
|
||||
import com.annimon.ownlang.parser.Parser;
|
||||
import com.annimon.ownlang.parser.SourceLoader;
|
||||
@ -38,12 +37,7 @@ public final class IncludeStatement extends InterruptableNode implements Stateme
|
||||
public Statement loadProgram(String path) throws IOException {
|
||||
final String input = SourceLoader.readSource(path);
|
||||
final List<Token> tokens = Lexer.tokenize(input);
|
||||
final Parser parser = new Parser(tokens);
|
||||
final Statement program = parser.parse();
|
||||
if (parser.getParseErrors().hasErrors()) {
|
||||
throw new ParseException(parser.getParseErrors().toString());
|
||||
}
|
||||
return program;
|
||||
return Parser.parse(tokens);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.annimon.ownlang.parser;
|
||||
package com.annimon.ownlang.parser.error;
|
||||
|
||||
import com.annimon.ownlang.parser.Range;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.annimon.ownlang.parser;
|
||||
package com.annimon.ownlang.parser.error;
|
||||
|
||||
import com.annimon.ownlang.Console;
|
||||
import java.util.ArrayList;
|
@ -1,6 +1,6 @@
|
||||
package com.annimon.ownlang.parser;
|
||||
|
||||
import com.annimon.ownlang.exceptions.LexerException;
|
||||
import com.annimon.ownlang.exceptions.OwnLangParserException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
@ -108,7 +108,7 @@ public class LexerTest {
|
||||
@MethodSource("invalidData")
|
||||
public void testInvalidInput(String name, String input) throws IOException {
|
||||
assertThatThrownBy(() -> Lexer.tokenize(input))
|
||||
.isInstanceOf(LexerException.class);
|
||||
.isInstanceOf(OwnLangParserException.class);
|
||||
}
|
||||
|
||||
private static void assertTokens(List<Token> result, TokenType... tokenTypes) {
|
||||
|
@ -2,7 +2,7 @@ package com.annimon.ownlang.utils;
|
||||
|
||||
import com.annimon.ownlang.Console;
|
||||
import com.annimon.ownlang.Version;
|
||||
import com.annimon.ownlang.exceptions.LexerException;
|
||||
import com.annimon.ownlang.exceptions.OwnLangParserException;
|
||||
import com.annimon.ownlang.exceptions.StoppedException;
|
||||
import com.annimon.ownlang.lib.Functions;
|
||||
import com.annimon.ownlang.lib.UserDefinedFunction;
|
||||
@ -84,7 +84,7 @@ public final class Repl {
|
||||
}
|
||||
}
|
||||
program.execute();
|
||||
} catch (LexerException lex) {
|
||||
} catch (OwnLangParserException lex) {
|
||||
continue;
|
||||
} catch (StoppedException ex) {
|
||||
// skip
|
||||
|
Loading…
Reference in New Issue
Block a user