Explorar el Código

Add calculator plugin

Change-Id: I3372fcf5db4539d21192d663caaa052394377a2c
Reviewed-on: http://gerrit.dmdirc.com/276
Reviewed-by: Shane Mc Cormack <shane@dmdirc.com>
Tested-by: Gregory Holmes <greboid@dmdirc.com>
tags/0.6.3
Chris Smith hace 14 años
padre
commit
e98eb31c38

+ 84
- 0
src/com/dmdirc/addons/calc/CalcCommand.java Ver fichero

@@ -0,0 +1,84 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import com.dmdirc.commandparser.CommandArguments;
26
+import com.dmdirc.commandparser.commands.GlobalCommand;
27
+import com.dmdirc.ui.interfaces.InputWindow;
28
+import java.text.ParseException;
29
+
30
+/**
31
+ * A command which allows users to evaluate various mathematical expressions,
32
+ * and perform basic calculations.
33
+ *
34
+ * @author chris
35
+ */
36
+public class CalcCommand extends GlobalCommand {
37
+
38
+    /** {@inheritDoc} */
39
+    @Override
40
+    public void execute(final InputWindow origin, final boolean isSilent,
41
+            final CommandArguments args) {
42
+        try {
43
+            int offset = 0;
44
+            boolean showexpr = false;
45
+
46
+            if (args.getArguments().length > 0 && args.getArguments()[0].equals("--showexpr")) {
47
+                showexpr = true;
48
+                offset++;
49
+            }
50
+
51
+            final String input = args.getArgumentsAsString(offset);
52
+            final Lexer lexer = new Lexer(input);
53
+            final Parser parser = new Parser(lexer);
54
+            final Evaluator evaluator = new Evaluator(parser.parse());
55
+            final Number result = evaluator.evaluate();
56
+            sendLine(origin, isSilent, FORMAT_OUTPUT,
57
+                    (showexpr ? input + " = " : "") + result);
58
+        } catch (ParseException ex) {
59
+            sendLine(origin, isSilent, FORMAT_ERROR, "Unable to parse expression: "
60
+                    + ex.getMessage());
61
+        } catch (ArithmeticException ex) {
62
+            sendLine(origin, isSilent, FORMAT_ERROR, "Unable to calculate expression: "
63
+                    + ex.getMessage());
64
+        }
65
+    }
66
+
67
+    /** {@inheritDoc} */
68
+    @Override
69
+    public String getName() {
70
+        return "calc";
71
+    }
72
+
73
+    /** {@inheritDoc} */
74
+    @Override
75
+    public boolean showInHelp() {
76
+        return true;
77
+    }
78
+
79
+    /** {@inheritDoc} */
80
+    @Override
81
+    public String getHelp() {
82
+        return "calc [--showexpr] <expr> - evaluate mathematical expression";
83
+    }
84
+}

+ 58
- 0
src/com/dmdirc/addons/calc/CalcCommandInfo.java Ver fichero

@@ -0,0 +1,58 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import com.dmdirc.commandparser.CommandInfo;
26
+import com.dmdirc.commandparser.CommandType;
27
+
28
+/**
29
+ * Calc command info.
30
+ *
31
+ * @author chris
32
+ */
33
+public class CalcCommandInfo implements CommandInfo {
34
+
35
+    /** {@inheritDoc} */
36
+    @Override
37
+    public String getName() {
38
+        return "calc";
39
+    }
40
+
41
+    /** {@inheritDoc} */
42
+    @Override
43
+    public boolean showInHelp() {
44
+        return true;
45
+    }
46
+
47
+    /** {@inheritDoc} */
48
+    @Override
49
+    public String getHelp() {
50
+        return "calc [--showexpr] <expr> - evaluate mathematical expression";
51
+    }
52
+
53
+    /** {@inheritDoc} */
54
+    @Override
55
+    public CommandType getType() {
56
+        return CommandType.TYPE_GLOBAL;
57
+    }
58
+}

+ 52
- 0
src/com/dmdirc/addons/calc/CalcPlugin.java Ver fichero

@@ -0,0 +1,52 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import com.dmdirc.commandparser.CommandManager;
26
+import com.dmdirc.plugins.Plugin;
27
+
28
+/**
29
+ * A plugin which parses and evaluates various mathematical expressions.
30
+ * 
31
+ * @author chris
32
+ */
33
+public class CalcPlugin extends Plugin {
34
+
35
+    /** The command we register when loaded. */
36
+    private final CalcCommand command = new CalcCommand();
37
+    /** The CommandInfo object describing the calc command. */
38
+    private final CalcCommandInfo commandInfo = new CalcCommandInfo();
39
+
40
+    /** {@inheritDoc} */
41
+    @Override
42
+    public void onLoad() {
43
+        CommandManager.registerCommand(command, commandInfo);
44
+    }
45
+
46
+    /** {@inheritDoc} */
47
+    @Override
48
+    public void onUnload() {
49
+        CommandManager.unregisterCommand(commandInfo);
50
+    }
51
+
52
+}

+ 84
- 0
src/com/dmdirc/addons/calc/DotOutputter.java Ver fichero

@@ -0,0 +1,84 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.text.ParseException;
26
+
27
+/**
28
+ * Outputs a string which can be read by the unix `dot` utility to show a
29
+ * directed graph showing the tokens parsed by a {@link Parser}.
30
+ *
31
+ * @author chris
32
+ */
33
+public class DotOutputter {
34
+
35
+    /** The parser which will be read. */
36
+    private final Parser parser;
37
+
38
+    /** Counter for assigning node IDs. */
39
+    private int nodes = 0;
40
+
41
+    /**
42
+     * Creates a new DotOutputter for the specified parser.
43
+     *
44
+     * @param parser The parser whose output should be shown
45
+     */
46
+    public DotOutputter(final Parser parser) {
47
+        this.parser = parser;
48
+    }
49
+
50
+    /**
51
+     * Outputs a string which may be read by the `dot` utility to create a
52
+     * directed graph of tokens.
53
+     *
54
+     * @return A string describing the parse tree from the specified parser
55
+     * @throws ParseException If the parser encounters an exception
56
+     */
57
+    public String output() throws ParseException {
58
+        return "digraph astoutput { " + output(0, parser.parse()) + " }";
59
+    }
60
+
61
+    /**
62
+     * Outputs the relevant text for the specified token, assigning it an ID
63
+     * specified by the node parameter.
64
+     *
65
+     * @param node The ID of the node to be used in the output
66
+     * @param token The token to be outputted
67
+     * @return A string corresponding to the dot representation of the
68
+     * specified node and its children
69
+     */
70
+    protected String output(final int node, final TreeToken token) {
71
+        String out = "node" + node + " [label=\"" + token.getToken().getType()
72
+                + "\\n" + token.getToken().getContent() + "\"];";
73
+
74
+        for (TreeToken child : token.getChildren()) {
75
+            int id = ++nodes;
76
+
77
+            out += output(id, child);
78
+            out += "node" + node + " -> node" + id + ";";
79
+        }
80
+
81
+        return out;
82
+    }
83
+
84
+}

+ 54
- 0
src/com/dmdirc/addons/calc/Evaluator.java Ver fichero

@@ -0,0 +1,54 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+/**
26
+ * A simple evaluator which just requests that the {@link TreeToken} evaluates
27
+ * itself.
28
+ *
29
+ * @author chris
30
+ */
31
+public class Evaluator {
32
+
33
+    /** The token to be evaluated. */
34
+    private final TreeToken node;
35
+
36
+    /**
37
+     * Creates a new evaluator for the specified token.
38
+     *
39
+     * @param node The token to be evaluated
40
+     */
41
+    public Evaluator(final TreeToken node) {
42
+        this.node = node;
43
+    }
44
+
45
+    /**
46
+     * Evaluates the token and returns the numeric result.
47
+     *
48
+     * @return The numeric result of evaluation
49
+     */
50
+    public Number evaluate() {
51
+        return node.evaluate();
52
+    }
53
+
54
+}

+ 91
- 0
src/com/dmdirc/addons/calc/Lexer.java Ver fichero

@@ -0,0 +1,91 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.text.ParseException;
26
+import java.util.ArrayList;
27
+import java.util.Arrays;
28
+import java.util.List;
29
+
30
+/**
31
+ * The lexer takes a String input and produces an ordered list of {@link Token}s
32
+ * corresponding to the input.
33
+ *
34
+ * @author chris
35
+ */
36
+public class Lexer {
37
+
38
+    /** The input string. */
39
+    final String input;
40
+
41
+    /**
42
+     * Creates a new lexer for the specified input string.
43
+     *
44
+     * @param input The string to be tokenised
45
+     */
46
+    public Lexer(final String input) {
47
+        this.input = input.replaceAll("\\s+", "");
48
+    }
49
+
50
+    /**
51
+     * Tokenises the input string into an ordered list of tokens.
52
+     *
53
+     * @return A list of tokens corresponding to the input string
54
+     * @throws ParseException If an expected token is not found
55
+     */
56
+    public List<Token> tokenise() throws ParseException {
57
+        final List<Token> res = new ArrayList<Token>();
58
+        List<TokenType> possibles = Arrays.asList(TokenType.values());
59
+
60
+        boolean cont = true;
61
+        int i = 0;
62
+
63
+        do {
64
+            boolean found = false;
65
+
66
+            for (TokenType type : possibles) {
67
+                final int match = type.match(input, i);
68
+
69
+                if (match > -1) {
70
+                    res.add(new Token(type, input.substring(i, match)));
71
+
72
+                    possibles = type.getFollowers();
73
+                    i = match;
74
+                    found = true;
75
+                    cont = type != TokenType.END;
76
+
77
+                    break;
78
+                }
79
+            }
80
+
81
+            if (!found) {
82
+                throw new ParseException("No legal token found at offset "
83
+                        + i + ". Expecting one of: "
84
+                        + Arrays.toString(possibles.toArray()), i);
85
+            }
86
+        } while (cont);
87
+
88
+        return res;
89
+    }
90
+
91
+}

+ 240
- 0
src/com/dmdirc/addons/calc/Parser.java Ver fichero

@@ -0,0 +1,240 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.text.ParseException;
26
+import java.util.ArrayList;
27
+import java.util.Arrays;
28
+import java.util.Collections;
29
+import java.util.Comparator;
30
+import java.util.List;
31
+
32
+/**
33
+ * The parser takes the output from a {@link Lexer} and applies precdence rules
34
+ * to build the tokens into a tree.
35
+ *
36
+ * @author chris
37
+ */
38
+public class Parser {
39
+
40
+    /** The lexer whose output will be parsed. */
41
+    protected final Lexer lexer;
42
+    /** A list of token types sorted by their precendece. */
43
+    protected static final List<TokenType> TOKENS_BY_PRECEDENCE;
44
+
45
+    static {
46
+        TOKENS_BY_PRECEDENCE = new ArrayList<TokenType>(Arrays.asList(TokenType.values()));
47
+        Collections.sort(TOKENS_BY_PRECEDENCE, new TokenTypePrecedenceComparator());
48
+    }
49
+
50
+    /**
51
+     * Creates a new parser for the specified lexer.
52
+     *
53
+     * @param lexer The lexer whose output should be parsed
54
+     */
55
+    public Parser(final Lexer lexer) {
56
+        this.lexer = lexer;
57
+    }
58
+
59
+    /**
60
+     * Parses the output of this parser's lexer, and returns a {@link TreeToken}
61
+     * representing the parsed formula.
62
+     *
63
+     * @return A token tree corresponding to the lexer's token output
64
+     * @throws ParseException If the lexer encounters a parse error, or if an
65
+     * error occurs while parsing the lexer's output (such as a non-sensical
66
+     * formula such as one involving a mis-matched bracket).
67
+     */
68
+    public TreeToken parse() throws ParseException {
69
+        final List<TreeToken> tokens = new ArrayList<TreeToken>();
70
+
71
+        for (Token token : lexer.tokenise()) {
72
+            tokens.add(new TreeToken(token));
73
+        }
74
+
75
+        return parse(tokens);
76
+    }
77
+
78
+    /**
79
+     * Parses the specified tokens into a tree.
80
+     *
81
+     * @param tokens The tokens to be parsed
82
+     * @return A single tree containing all of the specified tokens
83
+     * @throws ParseException If the tokens contain mismatched brackets
84
+     */
85
+    protected TreeToken parse(final List<TreeToken> tokens) throws ParseException {
86
+        while (tokens.size() > 1) {
87
+            for (TokenType type : TOKENS_BY_PRECEDENCE) {
88
+                final int offset = findTokenType(tokens, type);
89
+
90
+                if (offset > -1) {
91
+                    switch (type.getArity()) {
92
+                        case HIDDEN:
93
+                            parseHiddenOperator(tokens, offset);
94
+                            break;
95
+                        case BINARY:
96
+                            parseBinaryOperator(tokens, offset);
97
+                            break;
98
+                        case UNARY:
99
+                            parseUnaryOperator(tokens, offset);
100
+                            break;
101
+                        case NULLARY:
102
+                            parseNullaryOperator(tokens, offset);
103
+                            break;
104
+                    }
105
+
106
+                    break;
107
+                }
108
+            }
109
+        }
110
+
111
+        return tokens.get(0);
112
+    }
113
+
114
+    /**
115
+     * Parses an operator that takes no operands.
116
+     *
117
+     * @param tokens The supply of tokens from which the operator will be parsed
118
+     * @param offset The offset at which the operator occurs
119
+     * @throws ParseException If the operator is a bracket and that bracket is
120
+     * mismatched
121
+     */
122
+    protected void parseNullaryOperator(final List<TreeToken> tokens, final int offset)
123
+            throws ParseException {
124
+        if (tokens.get(offset).getToken().getType() == TokenType.BRACKET_CLOSE
125
+                || tokens.get(offset).getToken().getType() == TokenType.BRACKET_OPEN) {
126
+            parseBracket(tokens, offset);
127
+        } else {
128
+            parseNumber(tokens, offset);
129
+        }
130
+    }
131
+
132
+    /**
133
+     * Parses a bracket operator.
134
+     *
135
+     * @param tokens The supply of tokens from which the operator will be parsed
136
+     * @param offset The offset at which the operator occurs
137
+     * @throws ParseException If the operator is a bracket and that bracket is
138
+     * mismatched
139
+     */
140
+    protected void parseBracket(final List<TreeToken> tokens, final int offset)
141
+            throws ParseException {
142
+        final List<TreeToken> stack = new ArrayList<TreeToken>();
143
+
144
+        for (int i = offset - 1; i > 0; i--) {
145
+            if (tokens.get(i).getToken().getType() == TokenType.BRACKET_OPEN
146
+                    && !tokens.get(i).isProcessed()) {
147
+                tokens.add(i, parse(stack));
148
+                tokens.get(i).setProcessed();
149
+                tokens.remove(i + 1);
150
+                tokens.remove(i + 1);
151
+                return;
152
+            } else {
153
+                stack.add(0, tokens.get(i));
154
+                tokens.remove(i);
155
+            }
156
+        }
157
+
158
+        throw new ParseException("Couldn't find matching opening bracket", offset);
159
+    }
160
+
161
+    /**
162
+     * Parses an operator that takes two operands.
163
+     *
164
+     * @param tokens The supply of tokens from which the operator will be parsed
165
+     * @param offset The offset at which the operator occurs
166
+     */
167
+    protected void parseBinaryOperator(final List<TreeToken> tokens, final int offset) {
168
+        tokens.get(offset).addChild(tokens.get(offset - 1));
169
+        tokens.get(offset).addChild(tokens.get(offset + 1));
170
+        tokens.get(offset).setProcessed();
171
+
172
+        tokens.remove(offset + 1);
173
+        tokens.remove(offset - 1);
174
+    }
175
+
176
+    /**
177
+     * Parses an operator that takes one operand.
178
+     *
179
+     * @param tokens The supply of tokens from which the operator will be parsed
180
+     * @param offset The offset at which the operator occurs
181
+     */
182
+    protected void parseUnaryOperator(final List<TreeToken> tokens, final int offset) {
183
+        tokens.get(offset).addChild(tokens.get(offset + 1));
184
+        tokens.get(offset).setProcessed();
185
+        tokens.remove(offset + 1);
186
+    }
187
+
188
+    /**
189
+     * Parses an operator that does not actually correspond to a piece of the
190
+     * input (such as the START and END operators).
191
+     *
192
+     * @param tokens The supply of tokens from which the operator will be parsed
193
+     * @param offset The offset at which the operator occurs
194
+     */
195
+    protected void parseHiddenOperator(final List<TreeToken> tokens, final int offset) {
196
+        tokens.remove(offset);
197
+    }
198
+
199
+    /**
200
+     * Parses a number.
201
+     *
202
+     * @param tokens The supply of tokens from which the operator will be parsed
203
+     * @param offset The offset at which the operator occurs
204
+     */
205
+    protected void parseNumber(final List<TreeToken> tokens, final int offset) {
206
+        tokens.get(offset).setProcessed();
207
+    }
208
+
209
+    /**
210
+     * Retrieves the offset of the first token within the input list that has
211
+     * a type corresponding to the specified {@link TokenType}.
212
+     *
213
+     * @param tokens The tokens to be searched
214
+     * @param type The desired token type
215
+     * @return The index of the first token with that type, or -1 if none found
216
+     */
217
+    protected static int findTokenType(final List<TreeToken> tokens, final TokenType type) {
218
+        for (int i = 0; i < tokens.size(); i++) {
219
+            if (tokens.get(i).getToken().getType() == type && !tokens.get(i).isProcessed()) {
220
+                return i;
221
+            }
222
+        }
223
+
224
+        return -1;
225
+    }
226
+
227
+    /**
228
+     * A class which compares token types based on their precendence.
229
+     */
230
+    protected static class TokenTypePrecedenceComparator implements Comparator<TokenType> {
231
+
232
+        /** {@inheritDoc} */
233
+        @Override
234
+        public int compare(final TokenType o1, final TokenType o2) {
235
+            return o2.getPrecedence() - o1.getPrecedence();
236
+        }
237
+
238
+    }
239
+
240
+}

+ 74
- 0
src/com/dmdirc/addons/calc/Token.java Ver fichero

@@ -0,0 +1,74 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+/**
26
+ * Describes a distinct piece of a mathematical formula, as tokenised by a
27
+ * {@link Lexer}.
28
+ *
29
+ * @author chris
30
+ */
31
+public class Token {
32
+
33
+    /** The type of this token. */
34
+    private final TokenType type;
35
+
36
+    /** The content of this token, if any. */
37
+    private final String content;
38
+
39
+    /**
40
+     * Creates a new token.
41
+     *
42
+     * @param type The type of the token
43
+     * @param content The content of the token
44
+     */
45
+    public Token(final TokenType type, final String content) {
46
+        this.type = type;
47
+        this.content = content;
48
+    }
49
+
50
+    /**
51
+     * Retrieves the content of this token.
52
+     *
53
+     * @return This token's content
54
+     */
55
+    public String getContent() {
56
+        return content;
57
+    }
58
+
59
+    /**
60
+     * Retrieves the type of this token.
61
+     *
62
+     * @return This token's type
63
+     */
64
+    public TokenType getType() {
65
+        return type;
66
+    }
67
+
68
+    /** {@inheritDoc} */
69
+    @Override
70
+    public String toString() {
71
+        return "[type: " + type + "; content: " + content + "]";
72
+    }
73
+
74
+}

+ 254
- 0
src/com/dmdirc/addons/calc/TokenType.java Ver fichero

@@ -0,0 +1,254 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.util.ArrayList;
26
+import java.util.List;
27
+import java.util.regex.Matcher;
28
+import java.util.regex.Pattern;
29
+
30
+/**
31
+ * Describes the different types of possible token, their arities, precedence,
32
+ * and the types of token that may follow them.
33
+ * 
34
+ * @author chris
35
+ */
36
+public enum TokenType {
37
+
38
+    /** The start of an input string. */
39
+    START(TokenTypeArity.HIDDEN, "^", 0, "NUMBER_*", "BRACKET_OPEN", "MOD_*"),
40
+    /** The end of an input string. */
41
+    END(TokenTypeArity.HIDDEN, "$", 0),
42
+
43
+    /** An opening bracket. */
44
+    BRACKET_OPEN(TokenTypeArity.NULLARY, "\\(", 0, "NUMBER_*", "MOD_*", "BRACKET_OPEN"),
45
+    /** A closing bracket. */
46
+    BRACKET_CLOSE(TokenTypeArity.NULLARY, "\\)", 50, "OP_*", "BRACKET_*", "END"),
47
+
48
+    /** A floating point number. */
49
+    NUMBER_FLOAT(TokenTypeArity.NULLARY, "[0-9]+\\.[0-9]+", 1, "OP_*", "BRACKET_*", "END") {
50
+        /** {@inheritDoc} */
51
+        @Override
52
+        public Number evaluate(final TreeToken token) {
53
+            return Float.valueOf(token.getToken().getContent());
54
+        }
55
+    },
56
+
57
+    /** An integer. */
58
+    NUMBER_INT(TokenTypeArity.NULLARY, "[0-9]+", 1, "OP_*", "BRACKET_*", "END") {
59
+        /** {@inheritDoc} */
60
+        @Override
61
+        public Number evaluate(final TreeToken token) {
62
+            return Float.valueOf(token.getToken().getContent());
63
+        }
64
+    },
65
+
66
+    /** A modifier signalling the following number is positive. */
67
+    MOD_POSITIVE(TokenTypeArity.UNARY, "\\+", 100, "NUMBER_*") {
68
+        /** {@inheritDoc} */
69
+        @Override
70
+        public Number evaluate(final TreeToken token) {
71
+            return token.getChildren().get(0).evaluate();
72
+        }
73
+    },
74
+
75
+    /** A modifier signalling the following number is negative. */
76
+    MOD_NEGATIVE(TokenTypeArity.UNARY, "-", 100, "NUMBER_*") {
77
+        /** {@inheritDoc} */
78
+        @Override
79
+        public Number evaluate(final TreeToken token) {
80
+            return -1 * token.getChildren().get(0).evaluate().floatValue();
81
+        }
82
+    },
83
+
84
+    /** The addition operator. */
85
+    OP_PLUS(TokenTypeArity.BINARY, "\\+", 7, "NUMBER_*", "BRACKET_OPEN") {
86
+        /** {@inheritDoc} */
87
+        @Override
88
+        public Number evaluate(final TreeToken token) {
89
+            return token.getChildren().get(0).evaluate().floatValue()
90
+                    + token.getChildren().get(1).evaluate().floatValue();
91
+        }
92
+    },
93
+
94
+    /** The subtraction operator. */
95
+    OP_MINUS(TokenTypeArity.BINARY, "-", 6, "NUMBER_*", "BRACKET_OPEN") {
96
+        /** {@inheritDoc} */
97
+        @Override
98
+        public Number evaluate(final TreeToken token) {
99
+            return token.getChildren().get(0).evaluate().floatValue()
100
+                    - token.getChildren().get(1).evaluate().floatValue();
101
+        }
102
+    },
103
+
104
+    /** The multiplication operator. */
105
+    OP_MULT(TokenTypeArity.BINARY, "(?=\\()|\\*", 9, "NUMBER_*", "BRACKET_OPEN") {
106
+        /** {@inheritDoc} */
107
+        @Override
108
+        public Number evaluate(final TreeToken token) {
109
+            return token.getChildren().get(0).evaluate().floatValue()
110
+                    * token.getChildren().get(1).evaluate().floatValue();
111
+        }
112
+    },
113
+
114
+    /** The division operator. */
115
+    OP_DIVIDE(TokenTypeArity.BINARY, "/", 10, "NUMBER_*", "BRACKET_OPEN") {
116
+        /** {@inheritDoc} */
117
+        @Override
118
+        public Number evaluate(final TreeToken token) {
119
+            return token.getChildren().get(0).evaluate().floatValue()
120
+                    / token.getChildren().get(1).evaluate().floatValue();
121
+        }
122
+    },
123
+
124
+    /** The modulo operator. */
125
+    OP_MOD(TokenTypeArity.BINARY, "%", 8, "NUMBER_*", "BRACKET_OPEN") {
126
+        /** {@inheritDoc} */
127
+        @Override
128
+        public Number evaluate(final TreeToken token) {
129
+            return token.getChildren().get(0).evaluate().floatValue()
130
+                    % token.getChildren().get(1).evaluate().floatValue();
131
+        }
132
+    },
133
+
134
+    /** The power operator. */
135
+    OP_POWER(TokenTypeArity.BINARY, "\\^", 11, "NUMBER_*", "BRACKET_OPEN") {
136
+        /** {@inheritDoc} */
137
+        @Override
138
+        public Number evaluate(final TreeToken token) {
139
+            return new Float(Math.pow(token.getChildren().get(0).evaluate().doubleValue(),
140
+                    token.getChildren().get(1).evaluate().doubleValue()));
141
+        }
142
+    },
143
+    ;
144
+
145
+    /** The string representation of tokens that may follow this one. */
146
+    private final String[] strfollows;
147
+    /** The precedence of this token. */
148
+    private final int precedence;
149
+    /** The list of tokens that may follow this one. */
150
+    private List<TokenType> follows;
151
+    /** The regular expression used to match this token. */
152
+    private final Pattern regex;
153
+    /** The arity of this token. */
154
+    private final TokenTypeArity arity;
155
+
156
+    /**
157
+     * Creates a new token type with the specified arguments.
158
+     *
159
+     * @param arity The arity of this token
160
+     * @param regex The regular expression used to match this token
161
+     * @param precedence The precendence of this token
162
+     * @param follows The names of the tokens which may follow this one
163
+     */
164
+    TokenType(final TokenTypeArity arity, final String regex,
165
+            final int precedence, final String ... follows) {
166
+        this.arity = arity;
167
+        this.strfollows = follows;
168
+        this.precedence = precedence;
169
+        this.regex = Pattern.compile(regex);
170
+    }
171
+
172
+    /**
173
+     * Retrieves a list of token types that may follow this one.
174
+     *
175
+     * @return A list of this token type's possible followers
176
+     */
177
+    public synchronized List<TokenType> getFollowers() {
178
+        if (follows == null) {
179
+            follows = new ArrayList<TokenType>();
180
+
181
+            for (int i = 0; i < strfollows.length; i++) {
182
+                follows.addAll(searchValueOf(strfollows[i]));
183
+            }
184
+        }
185
+
186
+        return follows;
187
+    }
188
+
189
+    /**
190
+     * Retrieves the arity of this token type.
191
+     *
192
+     * @return This token type's arity
193
+     */
194
+    public TokenTypeArity getArity() {
195
+        return arity;
196
+    }
197
+
198
+    /**
199
+     * Retrieves the precedence of this token type.
200
+     *
201
+     * @return This token type's precedence
202
+     */
203
+    public int getPrecedence() {
204
+        return precedence;
205
+    }
206
+
207
+    /**
208
+     * Attempts to match this token type against the specified input string
209
+     * (starting at the specified offset).
210
+     *
211
+     * @param input The string to be matched
212
+     * @param offset The offset within the string to start at
213
+     * @return -1 if no match was made, otherwise the number of characters that
214
+     * were matched as part of this token type.
215
+     */
216
+    public int match(final String input, final int offset) {
217
+        final Matcher matcher = regex.matcher(input);
218
+        matcher.useAnchoringBounds(false);
219
+        matcher.useTransparentBounds(true);
220
+
221
+        return matcher.find(offset) && matcher.start() == offset ? matcher.end() : -1;
222
+    }
223
+
224
+    /**
225
+     * Evaluates the specified token of this token type into a number.
226
+     *
227
+     * @param token The token to be evaluated
228
+     * @return A numerical representation of the specified token
229
+     */
230
+    public Number evaluate(final TreeToken token) {
231
+        throw new AbstractMethodError("Can't evaluate this token type");
232
+    }
233
+
234
+    /**
235
+     * Retrieves a list of types which match the specified name. The name
236
+     * may end with an asterisk (*), which is treated as a wild card.
237
+     *
238
+     * @param name The name to be searched for
239
+     * @return A list of matching tokens
240
+     */
241
+    protected static List<TokenType> searchValueOf(final String name) {
242
+        final List<TokenType> res = new ArrayList<TokenType>();
243
+
244
+        for (TokenType token : values()) {
245
+            if ((name.endsWith("*")
246
+                    && token.name().startsWith(name.substring(0, name.length() - 1)))
247
+                    || name.equals(token.name())) {
248
+                res.add(token);
249
+            }
250
+        }
251
+
252
+        return res;
253
+    }
254
+}

+ 44
- 0
src/com/dmdirc/addons/calc/TokenTypeArity.java Ver fichero

@@ -0,0 +1,44 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+/**
26
+ * Describes the arity (number of operands) of a token type.
27
+ *
28
+ * @author chris
29
+ */
30
+public enum TokenTypeArity {
31
+
32
+    /**
33
+     * A special type which does not have any operands and does not correspond
34
+     * to any input (e.g. the special START and END tokens).
35
+     */
36
+    HIDDEN,
37
+    /** A type which takes no operands. */
38
+    NULLARY,
39
+    /** A type which takes one operand. */
40
+    UNARY,
41
+    /** A type which takes two operands. */
42
+    BINARY;
43
+
44
+}

+ 112
- 0
src/com/dmdirc/addons/calc/TreeToken.java Ver fichero

@@ -0,0 +1,112 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.util.ArrayList;
26
+import java.util.List;
27
+
28
+/**
29
+ * Describes a tree of {@link Token}s.
30
+ * 
31
+ * @author chris
32
+ */
33
+public class TreeToken {
34
+
35
+    /** The children of this node. */
36
+    private final List<TreeToken> children = new ArrayList<TreeToken>();
37
+
38
+    /** The token at the root of the tree. */
39
+    private final Token token;
40
+
41
+    /** Whether or not this tree has been processed. */
42
+    private boolean processed = false;
43
+
44
+    /**
45
+     * Creates a new tree with the specified token at the root.
46
+     *
47
+     * @param token The root token
48
+     */
49
+    public TreeToken(final Token token) {
50
+        this.token = token;
51
+    }
52
+
53
+    /**
54
+     * Retrieves the (direct) children of this tree.
55
+     *
56
+     * @return This tree's children
57
+     */
58
+    public List<TreeToken> getChildren() {
59
+        return children;
60
+    }
61
+
62
+    /**
63
+     * Retrieves the root token of this tree.
64
+     *
65
+     * @return This tree's token
66
+     */
67
+    public Token getToken() {
68
+        return token;
69
+    }
70
+
71
+    /**
72
+     * Adds the specified child to this tree.
73
+     *
74
+     * @param token The child to be added
75
+     */
76
+    public void addChild(final TreeToken token) {
77
+        children.add(token);
78
+    }
79
+
80
+    /**
81
+     * Determines if this tree has been processed.
82
+     *
83
+     * @return True if the tree has been processed, false otherwise
84
+     */
85
+    public boolean isProcessed() {
86
+        return processed;
87
+    }
88
+
89
+    /**
90
+     * Sets the processed flag of this tree to true.
91
+     */
92
+    public void setProcessed() {
93
+        processed = true;
94
+    }
95
+
96
+    /**
97
+     * Evaluates this tree to return a number.
98
+     *
99
+     * @return A numerical evaluation of this tree.
100
+     */
101
+    public Number evaluate() {
102
+        return token.getType().evaluate(this);
103
+    }
104
+
105
+    /** {@inheritDoc} */
106
+    @Override
107
+    public String toString() {
108
+        return "[token: " + token + "; children: " + children + "; processed: "
109
+                + processed + "]";
110
+    }
111
+
112
+}

+ 25
- 0
src/com/dmdirc/addons/calc/plugin.config Ver fichero

@@ -0,0 +1,25 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  version
10
+
11
+metadata:
12
+  author=Chris <chris@dmdirc.com>
13
+  mainclass=com.dmdirc.addons.calc.CalcPlugin
14
+  description=Performs calculations
15
+  name=calc
16
+  nicename=Calc Plugin
17
+
18
+updates:
19
+  id=37
20
+
21
+version:
22
+  friendly=1.0
23
+
24
+provides:
25
+  calc command

+ 73
- 0
test/com/dmdirc/addons/calc/EvaluatorTest.java Ver fichero

@@ -0,0 +1,73 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.text.ParseException;
26
+import java.util.Arrays;
27
+import java.util.List;
28
+import org.junit.Test;
29
+import org.junit.runner.RunWith;
30
+import org.junit.runners.Parameterized;
31
+
32
+import static org.junit.Assert.*;
33
+
34
+@RunWith(Parameterized.class)
35
+public class EvaluatorTest {
36
+
37
+    private final String input;
38
+    private final float output;
39
+
40
+    public EvaluatorTest(String input, String output) {
41
+        this.input = input;
42
+        this.output = Float.parseFloat(output);
43
+    }
44
+
45
+    @Test
46
+    public void testEvaluator() throws ParseException {
47
+        final Parser p = new Parser(new Lexer(input));
48
+        final Evaluator e = new Evaluator(p.parse());
49
+        assertEquals(output, e.evaluate().floatValue(), 0.01);
50
+    }
51
+
52
+    @Parameterized.Parameters
53
+    public static List<Object[]> data() {
54
+        final Object[][] data = {
55
+            {"1", "1"},
56
+            {"-1", "-1"},
57
+            {"1+1", "2"},
58
+            {"(1)", "1"},
59
+            {"(((1)))", "1"},
60
+            {"2(1*1)", "2"},
61
+            {"2+2*3/4-1", "2.5"},
62
+            {"1.0000(17.5+0.5)(1.000)", "18"},
63
+            {"2^3", "8"},
64
+        };
65
+
66
+        return Arrays.asList(data);
67
+    }
68
+
69
+    public static junit.framework.Test suite() {
70
+        return new junit.framework.JUnit4TestAdapter(EvaluatorTest.class);
71
+    }
72
+
73
+}

+ 126
- 0
test/com/dmdirc/addons/calc/LexerTest.java Ver fichero

@@ -0,0 +1,126 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.text.ParseException;
26
+import java.util.Arrays;
27
+import java.util.List;
28
+
29
+import org.junit.Test;
30
+import static org.junit.Assert.*;
31
+
32
+public class LexerTest {
33
+
34
+    @Test
35
+    public void testBasicNumber() throws ParseException {
36
+        final Lexer lexer = new Lexer("123");
37
+        final List<Token> tokens = lexer.tokenise();
38
+
39
+        assertEquals(3, tokens.size());
40
+        assertEquals(TokenType.START, tokens.get(0).getType());
41
+        assertEquals(TokenType.NUMBER_INT, tokens.get(1).getType());
42
+        assertEquals("123", tokens.get(1).getContent());
43
+        assertEquals(TokenType.END, tokens.get(2).getType());
44
+    }
45
+
46
+    @Test
47
+    public void testComplexString() throws ParseException {
48
+        final Lexer lexer = new Lexer("(123 / 2.0) * ((3)+\t   (-1))");
49
+        final List<Token> tokens = lexer.tokenise();
50
+
51
+        System.out.println(Arrays.toString(tokens.toArray()));
52
+
53
+        assertEquals(18, tokens.size());
54
+        assertEquals(TokenType.START, tokens.get(0).getType());
55
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(1).getType());
56
+        assertEquals(TokenType.NUMBER_INT, tokens.get(2).getType());
57
+        assertEquals("123", tokens.get(2).getContent());
58
+        assertEquals(TokenType.OP_DIVIDE, tokens.get(3).getType());
59
+        assertEquals(TokenType.NUMBER_FLOAT, tokens.get(4).getType());
60
+        assertEquals("2.0", tokens.get(4).getContent());
61
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(5).getType());
62
+        assertEquals(TokenType.OP_MULT, tokens.get(6).getType());
63
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(7).getType());
64
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(8).getType());
65
+        assertEquals(TokenType.NUMBER_INT, tokens.get(9).getType());
66
+        assertEquals("3", tokens.get(9).getContent());
67
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(10).getType());
68
+        assertEquals(TokenType.OP_PLUS, tokens.get(11).getType());
69
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(12).getType());
70
+        assertEquals(TokenType.MOD_NEGATIVE, tokens.get(13).getType());
71
+        assertEquals(TokenType.NUMBER_INT, tokens.get(14).getType());
72
+        assertEquals("1", tokens.get(14).getContent());
73
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(15).getType());
74
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(16).getType());
75
+        assertEquals(TokenType.END, tokens.get(17).getType());
76
+    }
77
+
78
+    @Test
79
+    public void testBrackets() throws ParseException {
80
+        final Lexer lexer = new Lexer("((1))");
81
+        final List<Token> tokens = lexer.tokenise();
82
+
83
+        assertEquals(7, tokens.size());
84
+        assertEquals(TokenType.START, tokens.get(0).getType());
85
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(1).getType());
86
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(2).getType());
87
+        assertEquals(TokenType.NUMBER_INT, tokens.get(3).getType());
88
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(4).getType());
89
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(5).getType());
90
+        assertEquals(TokenType.END, tokens.get(6).getType());
91
+    }
92
+
93
+    @Test
94
+    public void testImplicitMult() throws ParseException {
95
+        final Lexer lexer = new Lexer("1(2)(3)");
96
+        final List<Token> tokens = lexer.tokenise();
97
+
98
+        assertEquals(11, tokens.size());
99
+        assertEquals(TokenType.START, tokens.get(0).getType());
100
+        assertEquals(TokenType.NUMBER_INT, tokens.get(1).getType());
101
+        assertEquals(TokenType.OP_MULT, tokens.get(2).getType());
102
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(3).getType());
103
+        assertEquals(TokenType.NUMBER_INT, tokens.get(4).getType());
104
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(5).getType());
105
+        assertEquals(TokenType.OP_MULT, tokens.get(6).getType());
106
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(7).getType());
107
+        assertEquals(TokenType.NUMBER_INT, tokens.get(8).getType());
108
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(9).getType());
109
+        assertEquals(TokenType.END, tokens.get(10).getType());
110
+    }
111
+
112
+    private void doIllegalTest(final String input, final int offset) {
113
+        try {
114
+            fail(Arrays.toString(new Lexer(input).tokenise().toArray()));
115
+        } catch (ParseException ex) {
116
+            assertEquals(offset, ex.getErrorOffset());
117
+        }
118
+    }
119
+
120
+    @Test public void testIllegalEnd1() { doIllegalTest("", 0); }
121
+    @Test public void testIllegalEnd2() { doIllegalTest("3+", 2); }
122
+    @Test public void testIllegalEnd3() { doIllegalTest("(", 1); }
123
+    @Test public void testIllegalOp1() { doIllegalTest("1++2", 2); }
124
+    @Test public void testIllegalOp2() { doIllegalTest("*1", 0); }
125
+
126
+}

+ 186
- 0
test/com/dmdirc/addons/calc/ParserTest.java Ver fichero

@@ -0,0 +1,186 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.text.ParseException;
26
+import java.util.ArrayList;
27
+import java.util.List;
28
+import org.junit.Test;
29
+import static org.junit.Assert.*;
30
+
31
+public class ParserTest {
32
+
33
+    @Test
34
+    public void testTokenTypesByPrecedence() {
35
+        int last = Integer.MAX_VALUE;
36
+
37
+        for (TokenType type : Parser.TOKENS_BY_PRECEDENCE) {
38
+            assertTrue(type.getPrecedence() <= last);
39
+            last = type.getPrecedence();
40
+        }
41
+    }
42
+
43
+    @Test
44
+    public void testFindTokenType() {
45
+        final List<TreeToken> tokens = new ArrayList<TreeToken>();
46
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_CLOSE, null)));
47
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_CLOSE, null)));
48
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_OPEN, null)));
49
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_CLOSE, null)));
50
+        tokens.add(new TreeToken(new Token(TokenType.OP_DIVIDE, null)));
51
+
52
+        assertEquals(0, Parser.findTokenType(tokens, TokenType.BRACKET_CLOSE));
53
+        assertEquals(2, Parser.findTokenType(tokens, TokenType.BRACKET_OPEN));
54
+        assertEquals(4, Parser.findTokenType(tokens, TokenType.OP_DIVIDE));
55
+        assertEquals(-1, Parser.findTokenType(tokens, TokenType.OP_MULT));
56
+
57
+        tokens.get(0).setProcessed();
58
+        assertEquals(1, Parser.findTokenType(tokens, TokenType.BRACKET_CLOSE));
59
+    }
60
+
61
+    @Test
62
+    public void testParseBracket() throws ParseException {
63
+        final Parser parser = new Parser(null);
64
+
65
+        final List<TreeToken> tokens = new ArrayList<TreeToken>();
66
+        tokens.add(new TreeToken(new Token(TokenType.START, null)));
67
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_OPEN, null)));
68
+        tokens.add(new TreeToken(new Token(TokenType.NUMBER_INT, null)));
69
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_CLOSE, null)));
70
+        tokens.add(new TreeToken(new Token(TokenType.END, null)));
71
+
72
+        parser.parseBracket(tokens, 3);
73
+        assertEquals(3, tokens.size());
74
+        assertEquals(TokenType.START, tokens.get(0).getToken().getType());
75
+        assertEquals(TokenType.NUMBER_INT, tokens.get(1).getToken().getType());
76
+        assertEquals(TokenType.END, tokens.get(2).getToken().getType());
77
+    }
78
+
79
+    @Test
80
+    public void testParseBracket2() throws ParseException {
81
+        final Parser parser = new Parser(null);
82
+
83
+        final List<TreeToken> tokens = new ArrayList<TreeToken>();
84
+        tokens.add(new TreeToken(new Token(TokenType.START, null)));
85
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_OPEN, null)));
86
+        tokens.add(new TreeToken(new Token(TokenType.NUMBER_INT, null)));
87
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_OPEN, null)));
88
+        tokens.add(new TreeToken(new Token(TokenType.NUMBER_INT, null)));
89
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_CLOSE, null)));
90
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_CLOSE, null)));
91
+        tokens.add(new TreeToken(new Token(TokenType.END, null)));
92
+
93
+        parser.parseBracket(tokens, 5);
94
+        assertEquals(6, tokens.size());
95
+        assertEquals(TokenType.START, tokens.get(0).getToken().getType());
96
+        assertEquals(TokenType.BRACKET_OPEN, tokens.get(1).getToken().getType());
97
+        assertEquals(TokenType.NUMBER_INT, tokens.get(2).getToken().getType());
98
+        assertEquals(TokenType.NUMBER_INT, tokens.get(3).getToken().getType());
99
+        assertEquals(TokenType.BRACKET_CLOSE, tokens.get(4).getToken().getType());
100
+        assertEquals(TokenType.END, tokens.get(5).getToken().getType());
101
+    }
102
+
103
+    @Test
104
+    public void testParseNumber() {
105
+        final Parser parser = new Parser(null);
106
+
107
+        final List<TreeToken> tokens = new ArrayList<TreeToken>();
108
+        tokens.add(new TreeToken(new Token(TokenType.START, null)));
109
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_OPEN, null)));
110
+        tokens.add(new TreeToken(new Token(TokenType.NUMBER_INT, null)));
111
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_CLOSE, null)));
112
+        tokens.add(new TreeToken(new Token(TokenType.END, null)));
113
+
114
+        parser.parseNumber(tokens, 2);
115
+        assertEquals(5, tokens.size());
116
+        assertTrue(tokens.get(2).isProcessed());
117
+    }
118
+
119
+    @Test
120
+    public void testParseHidden() {
121
+        final Parser parser = new Parser(null);
122
+
123
+        final List<TreeToken> tokens = new ArrayList<TreeToken>();
124
+        tokens.add(new TreeToken(new Token(TokenType.START, null)));
125
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_OPEN, null)));
126
+        tokens.add(new TreeToken(new Token(TokenType.NUMBER_INT, null)));
127
+        tokens.add(new TreeToken(new Token(TokenType.BRACKET_CLOSE, null)));
128
+        tokens.add(new TreeToken(new Token(TokenType.END, null)));
129
+
130
+        parser.parseHiddenOperator(tokens, 0);
131
+        assertEquals(4, tokens.size());
132
+        assertTrue(tokens.get(0).getToken().getType() == TokenType.BRACKET_OPEN);
133
+
134
+        parser.parseHiddenOperator(tokens, 3);
135
+        assertEquals(3, tokens.size());
136
+        assertTrue(tokens.get(2).getToken().getType() == TokenType.BRACKET_CLOSE);
137
+    }
138
+
139
+    @Test
140
+    public void testParseUnaryOps() {
141
+        final Parser parser = new Parser(null);
142
+
143
+        final List<TreeToken> tokens = new ArrayList<TreeToken>();
144
+        tokens.add(new TreeToken(new Token(TokenType.START, null)));
145
+        tokens.add(new TreeToken(new Token(TokenType.MOD_NEGATIVE, null)));
146
+        tokens.add(new TreeToken(new Token(TokenType.NUMBER_INT, null)));
147
+        tokens.add(new TreeToken(new Token(TokenType.END, null)));
148
+
149
+        parser.parseUnaryOperator(tokens, 1);
150
+        assertEquals(3, tokens.size());
151
+        assertTrue(tokens.get(0).getToken().getType() == TokenType.START);
152
+        assertTrue(tokens.get(1).getToken().getType() == TokenType.MOD_NEGATIVE);
153
+        assertTrue(tokens.get(2).getToken().getType() == TokenType.END);
154
+
155
+        assertTrue(tokens.get(1).isProcessed());
156
+        assertEquals(1, tokens.get(1).getChildren().size());
157
+        assertEquals(TokenType.NUMBER_INT, tokens.get(1).getChildren().get(0).getToken().getType());
158
+    }
159
+
160
+    @Test
161
+    public void testParseBinaryOps() {
162
+        final Parser parser = new Parser(null);
163
+
164
+        final List<TreeToken> tokens = new ArrayList<TreeToken>();
165
+        tokens.add(new TreeToken(new Token(TokenType.START, null)));
166
+        tokens.add(new TreeToken(new Token(TokenType.NUMBER_INT, "15")));
167
+        tokens.add(new TreeToken(new Token(TokenType.OP_MINUS, null)));
168
+        tokens.add(new TreeToken(new Token(TokenType.NUMBER_INT, "10")));
169
+        tokens.add(new TreeToken(new Token(TokenType.END, null)));
170
+
171
+        parser.parseBinaryOperator(tokens, 2);
172
+
173
+        assertEquals(3, tokens.size());
174
+        assertTrue(tokens.get(0).getToken().getType() == TokenType.START);
175
+        assertTrue(tokens.get(1).getToken().getType() == TokenType.OP_MINUS);
176
+        assertTrue(tokens.get(2).getToken().getType() == TokenType.END);
177
+
178
+        assertTrue(tokens.get(1).isProcessed());
179
+        assertEquals(2, tokens.get(1).getChildren().size());
180
+        assertEquals(TokenType.NUMBER_INT, tokens.get(1).getChildren().get(0).getToken().getType());
181
+        assertEquals("15", tokens.get(1).getChildren().get(0).getToken().getContent());
182
+        assertEquals(TokenType.NUMBER_INT, tokens.get(1).getChildren().get(1).getToken().getType());
183
+        assertEquals("10", tokens.get(1).getChildren().get(1).getToken().getContent());
184
+    }
185
+
186
+}

+ 60
- 0
test/com/dmdirc/addons/calc/TokenTypeTest.java Ver fichero

@@ -0,0 +1,60 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.calc;
24
+
25
+import java.util.List;
26
+import org.junit.Test;
27
+
28
+import static org.junit.Assert.*;
29
+
30
+public class TokenTypeTest {
31
+
32
+    @Test
33
+    public void testSearchValueOf() {
34
+        List<TokenType> res = TokenType.searchValueOf("NUMBER_*");
35
+        assertEquals(2, res.size());
36
+        assertTrue(res.contains(TokenType.NUMBER_FLOAT));
37
+        assertTrue(res.contains(TokenType.NUMBER_INT));
38
+
39
+        res = TokenType.searchValueOf("START");
40
+        assertEquals(1, res.size());
41
+        assertTrue(res.contains(TokenType.START));
42
+    }
43
+
44
+    @Test
45
+    public void testMatch() {
46
+        assertEquals(0, TokenType.START.match("Foo", 0));
47
+        assertEquals(-1, TokenType.START.match("Foo", 1));
48
+        assertEquals(1, TokenType.NUMBER_INT.match("1+3", 0));
49
+        assertEquals(4, TokenType.NUMBER_INT.match("1234+3", 0));
50
+        assertEquals(3, TokenType.END.match("1+3", 3));
51
+        assertEquals(-1, TokenType.END.match("1+3", 2));
52
+    }
53
+
54
+    @Test
55
+    public void testGetFollowers() {
56
+        assertTrue(TokenType.END.getFollowers().isEmpty());
57
+        assertFalse(TokenType.START.getFollowers().isEmpty());
58
+    }
59
+
60
+}

Loading…
Cancelar
Guardar