import java.io.Reader;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;

public class FullLiteralFinder1 {
    private Reader source;
    private List<String> literals = new ArrayList<>();

    public FullLiteralFinder1(String fname) throws IOException {
        this.source = new FileReader(fname);

        int gotraw = source.read();
        // note, -1 is returned at EOF
        while (gotraw != -1) {
            char got = (char)gotraw;
            if (got == '"') {
                readLiteral();
            }
            else if (got == '\'') {
                gotraw = source.read();
                if (gotraw == -1) throw new RuntimeException("EOF reached while reading char literal");
                readChar((char)gotraw);
                gotraw = source.read();
                if (gotraw != (int)'\'') throw new RuntimeException("improperly closed char literal");
            }
            else if (got == '/') {
                // maybe start of a comment?
                gotraw = source.read();
                if (gotraw == -1) break;
                got = (char)gotraw;
                if (got == '/') {
                    readSingleLineComment();
                }
                else if (got == '*') {
                    readMultiLineComment();
                }
                else {
                    // go back one characer, in case it's the start of a string or something
                    continue;
                }
            }
            gotraw = source.read();
        }
    }

    private void processSource() throws IOException {
    }

    private void readLiteral() throws IOException {
        // Read in a single string literal, starting after the initial "
        int gotraw = source.read();
        StringBuilder sb = new StringBuilder();
        while (gotraw != -1) {
            char got = (char)gotraw;
            if (got == '"') {
                // end of string reached!
                literals.add(sb.toString());
                return;
            }
            else if (got == '\n') {
                throw new RuntimeException("newlines not allowed inside string literal");
            }
            else {
                sb.append(readChar(got));
            }
            gotraw = source.read();
        }

        // if we get here, then EOF was reached inside the literal
        throw new RuntimeException("EOF reached while reading string literal");
    }

    private char readChar(char got) throws IOException {
        if (got == '\\') {
            int gotraw = source.read();
            if (gotraw == -1) throw new RuntimeException("EOF reached while reading char literal");
            got = (char)gotraw;
            if (got == '\\') return '\\';
            else if (got == 'n') return '\n';
            else if (got == '\'') return '\'';
            else if (got == '"') return '"';
            else {
                throw new RuntimeException(String.format("Unrecognized escape sequence \\%c", got));
            }
        }
        else return got;
    }

    private void readSingleLineComment() throws IOException {
        int gotraw = source.read();
        while (gotraw != -1) {
            char got = (char)gotraw;
            if (got == '\n') {
                // comment ends with newline
                return;
            }
            gotraw = source.read();
        }
        // if EOF is reached, that's fine
    }

    private void readMultiLineComment() throws IOException {
        int gotraw = source.read();
        while (gotraw != -1) {
            char got = (char)gotraw;
            if (got == '*') {
                gotraw = source.read();
                got = (char)gotraw;
                if (got == '/') {
                    // comment ended with */
                    return;
                }
                else {
                    // go back to the loop beginning; otherwise this would skip
                    // the character after the *
                    continue;
                }
            }
            gotraw = source.read();
        }
        // reaching EOF here is an error
        throw new RuntimeException("EOF reached while reading multi-line comment");
    }

    static List<String> getLiterals(String fname) throws IOException {
        return new FullLiteralFinder1(fname).literals;
    }

    public static void main(String[] args) throws IOException {
        String fname = args[0];
        System.out.format("literals in %s:\n", fname);
        for (String literal : getLiterals(fname)) {
            System.out.format("  |%s|\n", literal);
        }
    }
}
