CFG.java 2.95 KB
Newer Older
Ethan Ordentlich's avatar
Ethan Ordentlich committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package cfg;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;


public class CFG {

    public static class Symbol {
        protected String value;

        public String getValue() {
            return this.value;
        }

        @Override
        public boolean equals(Object obj) {
            // Subclasses are considered not equal even if the value is the same
            if (obj == null || this.getClass() != obj.getClass()) {
                return false;
            }
            return value.equals(((Symbol) obj).value);
        }

        @Override
        public int hashCode() {
            // Subclasses should hash differently
            return Objects.hash(value, getClass());
        }

        @Override
        public String toString() {
            return this.value;
        }
    }

    public static class Terminal extends Symbol {
        public Terminal(String v) {
            // Use multiple terminals for longer strings
            assert v.length() == 1;
            this.value = v;
        }
    }

    public static class Nonterminal extends Symbol {
        public Nonterminal(String v) {
            this.value = v;
        }
    }

    public final Nonterminal start;
    private final Map<Nonterminal, List<Symbol[]>> ruleMap;

    public CFG(Nonterminal nt) {
        this.start = nt;
        this.ruleMap = new HashMap<>();
    }

    public CFG(String string) {
        this(nt(string));
    }

    public List<Symbol[]> getRules(Nonterminal symbol) {
        return Collections.unmodifiableList(ruleMap.getOrDefault(symbol, List.of()));
    }

    /**
     * Add a production to the grammar of the form lhs -> rhs
     *
     * @param lhs nonterminal on the LHS of the production
     * @param rhs Symbols on the RHS of the production, may be either terminal or nonterminal
     */
    public void addRule(Nonterminal lhs, Symbol... rhs) {
        this.ruleMap.putIfAbsent(lhs, new ArrayList<>());
        this.ruleMap.get(lhs).add(rhs);
    }

    // Various ergonomic ways of adding multiple rules for a single LHS

    /**
     * Add multiple productions for a single LHS to the grammar
     *
     * @param lhs  nonterminal on the LHS of the production
     * @param rhss Iterable of RHS's
     */
    public void addRules(Nonterminal lhs, Iterable<Symbol[]> rhss) {
        rhss.forEach(rhs -> addRule(lhs, rhs));
    }

    /**
     * Add multiple productions for a single LHS to the grammar
     *
     * @param lhs  nonterminal on the LHS of the production
     * @param rhss array of RHS's
     */
    public void addRules(Nonterminal lhs, Symbol[]... rhss) {
        Arrays.stream(rhss).forEach(rhs -> addRule(lhs, rhs));
    }

    // Shorthand constructors

    public static Terminal t(String v) {
        return new Terminal(v);
    }

    public static Nonterminal nt(String v) {
        return new Nonterminal(v);
    }
}