annotate at/ofai/music/util/Matcher.java @ 5:bcb4c9697967 tip

Add README and CITATION files
author Chris Cannam
date Tue, 03 Dec 2013 12:58:05 +0000
parents 4c3f5bc01c97
children
rev   line source
Chris@2 1 /*
Chris@2 2 Copyright (C) 2001, 2006 by Simon Dixon
Chris@2 3
Chris@2 4 This program is free software; you can redistribute it and/or modify
Chris@2 5 it under the terms of the GNU General Public License as published by
Chris@2 6 the Free Software Foundation; either version 2 of the License, or
Chris@2 7 (at your option) any later version.
Chris@2 8
Chris@2 9 This program is distributed in the hope that it will be useful,
Chris@2 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@2 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@2 12 GNU General Public License for more details.
Chris@2 13
Chris@2 14 You should have received a copy of the GNU General Public License along
Chris@2 15 with this program (the file gpl.txt); if not, download it from
Chris@2 16 http://www.gnu.org/licenses/gpl.txt or write to the
Chris@2 17 Free Software Foundation, Inc.,
Chris@2 18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Chris@2 19 */
Chris@2 20
Chris@2 21 package at.ofai.music.util;
Chris@2 22
Chris@2 23 /** A simple parser for Prolog-type notation, but only handling the subset of
Chris@2 24 * Prolog used in "match" files.
Chris@2 25 */
Chris@2 26 public class Matcher {
Chris@2 27
Chris@2 28 /** The unparsed part of the current line of text */
Chris@2 29 protected String s;
Chris@2 30
Chris@2 31 /** The constructor is initialised with the input line of text for parsing*/
Chris@2 32 public Matcher(String data) { s = data; }
Chris@2 33
Chris@2 34 /** Reinitialise the parser with a new line of input */
Chris@2 35 public void set(String data) { s = data; }
Chris@2 36
Chris@2 37 /** Return the unparsed part of the input line */
Chris@2 38 public String get() { return s; }
Chris@2 39
Chris@2 40 /** Returns true if there is input data remaining */
Chris@2 41 public boolean hasData() {
Chris@2 42 return (s != null) && (s.length() > 0);
Chris@2 43 } // hasData()
Chris@2 44
Chris@2 45 /** Matches a String with the unparsed input data.
Chris@2 46 * If the complete String occurs at the beginning of the unparsed data,
Chris@2 47 * the unparsed data is advanced to the end of the String; otherwise
Chris@2 48 * the data is left unchanged.
Chris@2 49 *
Chris@2 50 * @param m the String to match
Chris@2 51 * @return true if m matches the beginning of the unparsed data
Chris@2 52 */
Chris@2 53 public boolean matchString(String m) {
Chris@2 54 if (s.startsWith(m)) {
Chris@2 55 s = s.substring(m.length());
Chris@2 56 return true;
Chris@2 57 }
Chris@2 58 return false;
Chris@2 59 } // matchString()
Chris@2 60
Chris@2 61 /** Skips input up to and including the next instance of a given character.
Chris@2 62 * It is an error for the character not to occur in the data.
Chris@2 63 * @param c the character to skip to
Chris@2 64 */
Chris@2 65 public void skip(char c) {
Chris@2 66 int index = s.indexOf(c);
Chris@2 67 if (index >= 0)
Chris@2 68 s = s.substring(index + 1);
Chris@2 69 else
Chris@2 70 throw new RuntimeException("Parse error in skip(), expecting " + c);
Chris@2 71 } // skip()
Chris@2 72
Chris@2 73 /** Removes whitespace from the beginning and end of the line.
Chris@2 74 */
Chris@2 75 public void trimSpace() {
Chris@2 76 s = s.trim();
Chris@2 77 } // trimSpace()
Chris@2 78
Chris@2 79 /** Returns and consumes the next character of unparsed data. */
Chris@2 80 public char getChar() {
Chris@2 81 char c = s.charAt(0);
Chris@2 82 s = s.substring(1);
Chris@2 83 return c;
Chris@2 84 } // getChar()
Chris@2 85
Chris@2 86 /** Returns and consumes an int value from the head of the unparsed data. */
Chris@2 87 public int getInt() {
Chris@2 88 int sz = 0;
Chris@2 89 trimSpace();
Chris@2 90 while ((sz < s.length()) && (Character.isDigit(s.charAt(sz)) ||
Chris@2 91 ((sz==0) && (s.charAt(sz) == '-'))))
Chris@2 92 sz++;
Chris@2 93 int val = Integer.parseInt(s.substring(0, sz));
Chris@2 94 s = s.substring(sz);
Chris@2 95 return val;
Chris@2 96 } // getInt()
Chris@2 97
Chris@2 98 /** Returns and consumes a double value, with two limitations:
Chris@2 99 * 1) exponents are ignored e.g. 5.4e-3 is read as 5.4;
Chris@2 100 * 2) a value terminated by a 2nd "." causes an Exception to be thrown
Chris@2 101 */
Chris@2 102 public double getDouble() {
Chris@2 103 int sz = 0;
Chris@2 104 trimSpace();
Chris@2 105 while ((sz < s.length()) && (Character.isDigit(s.charAt(sz)) ||
Chris@2 106 ((sz==0)&&(s.charAt(sz) == '-')) || (s.charAt(sz) == '.')))
Chris@2 107 sz++;
Chris@2 108 double val = Double.parseDouble(s.substring(0, sz));
Chris@2 109 s = s.substring(sz);
Chris@2 110 return val;
Chris@2 111 } // getDouble()
Chris@2 112
Chris@2 113 /** Returns and consumes a string terminated by the first comma,
Chris@2 114 * parenthesis, bracket or brace. Equivalent to getString(false).
Chris@2 115 */
Chris@2 116 public String getString() {
Chris@2 117 return getString(false);
Chris@2 118 } // getString()
Chris@2 119
Chris@2 120 /**
Chris@2 121 * Returns and consumes a string terminated by various punctuation symbols.
Chris@2 122 * Terminators include: '(', '[', '{', ',', '}', ']' and ')'.
Chris@2 123 * An Exception is thrown if no terminator is found.
Chris@2 124 *
Chris@2 125 * @param extraPunctuation Specifies whether '-' and '.' are terminators
Chris@2 126 */
Chris@2 127 public String getString(boolean extraPunctuation) {
Chris@2 128 char[] stoppers = {'(','[','{',',','}',']',')','-','.'};
Chris@2 129 int index1 = s.indexOf(stoppers[0]);
Chris@2 130 for (int i = 1; i < stoppers.length - (extraPunctuation? 0:2); i++) {
Chris@2 131 int index2 = s.indexOf(stoppers[i]);
Chris@2 132 if (index1 >= 0) {
Chris@2 133 if ((index2 >= 0) && (index1 > index2))
Chris@2 134 index1 = index2;
Chris@2 135 } else
Chris@2 136 index1 = index2;
Chris@2 137 }
Chris@2 138 if (index1 < 0)
Chris@2 139 throw new RuntimeException("getString(): no terminator: " + s);
Chris@2 140 String val = s.substring(0, index1);
Chris@2 141 s = s.substring(index1);
Chris@2 142 return val;
Chris@2 143 } // getString()
Chris@2 144
Chris@2 145 /** Returns and consumes a comma-separated list of terms, surrounded by a
Chris@2 146 * matching set of parentheses, brackets or braces.
Chris@2 147 * The list may have any number of levels of recursion.
Chris@2 148 * @return The return value is a linked list of the terms
Chris@2 149 * (which themselves may be lists or String values)
Chris@2 150 */
Chris@2 151 public ListTerm getList() {
Chris@2 152 if ("([{".indexOf(s.charAt(0)) >= 0)
Chris@2 153 return new ListTerm(getChar());
Chris@2 154 return null;
Chris@2 155 } // getList()
Chris@2 156
Chris@2 157 /** Returns and consumes a Prolog-style predicate, consisting of a functor
Chris@2 158 * followed by an optional list of arguments in parentheses.
Chris@2 159 */
Chris@2 160 public Predicate getPredicate() {
Chris@2 161 return new Predicate();
Chris@2 162 } // getPredicate()
Chris@2 163
Chris@2 164 class Predicate {
Chris@2 165
Chris@2 166 String head;
Chris@2 167 ListTerm args;
Chris@2 168
Chris@2 169 protected Predicate() {
Chris@2 170 head = getString(true);
Chris@2 171 args = getList();
Chris@2 172 }
Chris@2 173
Chris@2 174 public Object arg(int index) {
Chris@2 175 ListTerm t = args;
Chris@2 176 for (int i = 0; i < index; i++)
Chris@2 177 t = t.next;
Chris@2 178 return t.term;
Chris@2 179 } // arg
Chris@2 180
Chris@2 181 public String toString() {
Chris@2 182 return (args == null)? head: head + args;
Chris@2 183 }
Chris@2 184
Chris@2 185 } // inner class Predicate
Chris@2 186
Chris@2 187 class ListTerm {
Chris@2 188
Chris@2 189 Object term;
Chris@2 190 ListTerm next;
Chris@2 191 char opener, closer;
Chris@2 192
Chris@2 193 protected ListTerm(char c) {
Chris@2 194 opener = c;
Chris@2 195 term = null;
Chris@2 196 next = null;
Chris@2 197 if (hasData()) {
Chris@2 198 switch(s.charAt(0)) {
Chris@2 199 case '(':
Chris@2 200 case '[':
Chris@2 201 case '{':
Chris@2 202 term = new ListTerm(getChar());
Chris@2 203 break;
Chris@2 204 default:
Chris@2 205 term = getString();
Chris@2 206 break;
Chris@2 207 }
Chris@2 208 }
Chris@2 209 if (hasData()) {
Chris@2 210 closer = getChar();
Chris@2 211 switch(closer) {
Chris@2 212 case ')':
Chris@2 213 if (opener == '(')
Chris@2 214 return;
Chris@2 215 break;
Chris@2 216 case ']':
Chris@2 217 if (opener == '[')
Chris@2 218 return;
Chris@2 219 break;
Chris@2 220 case '}':
Chris@2 221 if (opener == '{')
Chris@2 222 return;
Chris@2 223 break;
Chris@2 224 case ',':
Chris@2 225 next = new ListTerm(opener);
Chris@2 226 return;
Chris@2 227 }
Chris@2 228 }
Chris@2 229 throw new RuntimeException("Parse error in ListTerm(): " + s);
Chris@2 230 } // constructor
Chris@2 231
Chris@2 232 public String toString() {
Chris@2 233 String s = "" + opener;
Chris@2 234 for (ListTerm ptr = this; ptr != null; ptr = ptr.next)
Chris@2 235 s += ptr.term.toString() + ptr.closer;
Chris@2 236 return s;
Chris@2 237 } // toString()
Chris@2 238
Chris@2 239 } // inner class ListTerm
Chris@2 240
Chris@2 241 } // class Matcher
Chris@2 242