All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.firemage.autograder.treeg.RegExLexer Maven / Gradle / Ivy

package de.firemage.autograder.treeg;

import java.util.ArrayDeque;
import java.util.Deque;

public class RegExLexer {
    private final String content;
    private int index;
    private final Deque marks;

    public RegExLexer(String content) {
        this.content = content;
        this.index = 0;
        this.marks = new ArrayDeque<>();
    }

    public char consumeNext() throws InvalidRegExSyntaxException {
        char value = this.read(this.index);
        this.index++;
        return value;
    }

    public char peek() throws InvalidRegExSyntaxException {
        return this.peek(0);
    }

    public boolean peekInRange(char min, char max) throws InvalidRegExSyntaxException {
        char c = this.peek();
        return c >= min && c <= max;
    }

    public char peek(int offset) throws InvalidRegExSyntaxException {
        return this.read(this.index + offset);
    }

    public boolean hasNext() {
        return this.hasNext(1);
    }

    public boolean hasNext(int n) {
        return this.index <= this.length() - n;
    }

    public RegExElementType peekType() throws InvalidRegExSyntaxException {
        return this.peekType(0);
    }

    public RegExElementType peekType(int offset) throws InvalidRegExSyntaxException {
        if (!this.hasNext(offset + 1)) {
            return RegExElementType.EOF;
        }

        return switch (this.peek(offset)) {
            case '(' -> RegExElementType.GROUP_START;
            case ')' -> RegExElementType.GROUP_END;
            case '[' -> RegExElementType.CHARACTER_CLASS_START;
            case ']' -> RegExElementType.CHARACTER_CLASS_END;
            case '-' -> RegExElementType.RANGE;
            case '\\' -> RegExElementType.ESCAPE;
            case '^' -> RegExElementType.HAT;
            case '.' -> RegExElementType.DOT;
            case '$' -> RegExElementType.DOLLAR;
            case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> RegExElementType.NUMBER;
            case '|' -> RegExElementType.OR;
            case 0 -> RegExElementType.EOF;
            default -> RegExElementType.CHARACTER;
        };
    }

    public void expect(RegExElementType type) throws InvalidRegExSyntaxException {
        if (this.peekType() != type) {
            throw new InvalidRegExSyntaxException("Expected " + type + ", found '" + this.peek() + "'");
        }
        this.consumeNext();
    }

    public void expect(char c) throws InvalidRegExSyntaxException {
        if (this.peek() != c) {
            throw new InvalidRegExSyntaxException("Expected " + c + ", found '" + this.peek() + "'");
        }
        this.consumeNext();
    }

    public void mark() {
        this.marks.push(this.index);
    }

    public void backtrack() {
        this.index = this.marks.pop();
    }

    @Override
    public String toString() {
        StringBuilder underline = new StringBuilder();
        for (int i = 0; i < this.length() + 1; i++) {
            if (i == this.index) {
                underline.append("^");
            } else if (i == this.length()) {
                underline.append(" ");
            } else {
                underline.append(" ");
            }
        }
        return this.content + "\n" + underline;
    }

    private char read(int offset) throws InvalidRegExSyntaxException {
        if (offset < this.content.length()) {
            return this.content.charAt(offset);
        } else if (offset == this.content.length()) {
            return 0;
        } else {
            throw new InvalidRegExSyntaxException("End of input reached");
        }
    }

    public int length() {
        return this.content.length() + 1;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy