abouttreesummaryrefslogcommitdiff
path: root/srcs/juloo.keyboard2/KeyValueParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'srcs/juloo.keyboard2/KeyValueParser.java')
-rw-r--r--srcs/juloo.keyboard2/KeyValueParser.java299
1 files changed, 212 insertions, 87 deletions
diff --git a/srcs/juloo.keyboard2/KeyValueParser.java b/srcs/juloo.keyboard2/KeyValueParser.java
index 0a5ce17..488f5d3 100644
--- a/srcs/juloo.keyboard2/KeyValueParser.java
+++ b/srcs/juloo.keyboard2/KeyValueParser.java
@@ -1,14 +1,22 @@
package juloo.keyboard2;
+import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
Parse a key definition. The syntax for a key definition is:
+- [(symbol):(key_action)]
- [:(kind) (attributes):(payload)].
- If [str] doesn't start with a [:] character, it is interpreted as an
arbitrary string key.
+[key_action] is:
+- ['Arbitrary string']
+- [(key_action),(key_action),...]
+- [keyevent:(code)]
+- [(key_name)]
+
For the different kinds and attributes, see doc/Possible-key-values.md.
Examples:
@@ -18,103 +26,232 @@ Examples:
*/
public final class KeyValueParser
{
- static Pattern START_PAT;
- static Pattern ATTR_PAT;
+ static Pattern KEYDEF_TOKEN;
static Pattern QUOTED_PAT;
- static Pattern PAYLOAD_START_PAT;
static Pattern WORD_PAT;
- static public KeyValue parse(String str) throws ParseError
+ static public KeyValue parse(String input) throws ParseError
{
- String symbol = null;
- int flags = 0;
+ int symbol_ends = 0;
+ final int input_len = input.length();
+ while (symbol_ends < input_len && input.charAt(symbol_ends) != ':')
+ symbol_ends++;
+ if (symbol_ends == 0) // Old syntax
+ return Starting_with_colon.parse(input);
+ if (symbol_ends == input_len) // String key
+ return KeyValue.makeStringKey(input);
+ String symbol = input.substring(0, symbol_ends);
+ ArrayList<KeyValue> keydefs = new ArrayList<KeyValue>();
init();
- // Kind
- Matcher m = START_PAT.matcher(str);
- if (!m.lookingAt())
- parseError("Expected kind, for example \":str ...\".", m);
- String kind = m.group(1);
- // Attributes
- while (true)
- {
- if (!match(m, ATTR_PAT))
- break;
- String attr_name = m.group(1);
- String attr_value = parseSingleQuotedString(m);
- switch (attr_name)
- {
- case "flags":
- flags = parseFlags(attr_value, m);
- break;
- case "symbol":
- symbol = attr_value;
- break;
+ Matcher m = KEYDEF_TOKEN.matcher(input);
+ m.region(symbol_ends + 1, input_len);
+ do { keydefs.add(parse_key_def(m)); }
+ while (parse_comma(m));
+ for (KeyValue k : keydefs)
+ if (k == null)
+ parseError("Contains null key", m);
+ return KeyValue.makeMacro(symbol, keydefs.toArray(new KeyValue[]{}), 0);
+ }
- default:
- parseError("Unknown attribute "+attr_name, m);
- }
- }
- // Payload
- if (!match(m, PAYLOAD_START_PAT))
- parseError("Unexpected character", m);
- String payload;
- switch (kind)
+ static void init()
+ {
+ if (KEYDEF_TOKEN != null)
+ return;
+ KEYDEF_TOKEN = Pattern.compile("'|,|keyevent:|(?:[^\\\\',]+|\\\\.)+");
+ QUOTED_PAT = Pattern.compile("((?:[^'\\\\]+|\\\\')*)'");
+ WORD_PAT = Pattern.compile("[a-zA-Z0-9_]+|.");
+ }
+
+ static KeyValue key_by_name_or_str(String str)
+ {
+ KeyValue k = KeyValue.getSpecialKeyByName(str);
+ if (k != null)
+ return k;
+ return KeyValue.makeStringKey(str);
+ }
+
+ static KeyValue parse_key_def(Matcher m) throws ParseError
+ {
+ if (!match(m, KEYDEF_TOKEN))
+ parseError("Expected key definition", m);
+ String token = m.group(0);
+ switch (token)
{
- case "str":
- payload = parseSingleQuotedString(m);
- if (symbol == null)
- return KeyValue.makeStringKey(payload, flags);
- return KeyValue.makeStringKeyWithSymbol(payload, symbol, flags);
-
- case "char":
- payload = parsePayloadWord(m);
- if (payload.length() != 1)
- parseError("Expected a single character payload", m);
- return KeyValue.makeCharKey(payload.charAt(0), symbol, flags);
-
- case "keyevent":
- payload = parsePayloadWord(m);
- int eventcode = 0;
- try { eventcode = Integer.parseInt(payload); }
- catch (Exception _e)
- { parseError("Expected an integer payload", m); }
- if (symbol == null)
- symbol = String.valueOf(eventcode);
- return KeyValue.keyeventKey(symbol, eventcode, flags);
-
- default: break;
+ case "'": return parse_string_keydef(m);
+ case ",": parseError("Unexpected comma", m); return null;
+ case "keyevent:": return parse_keyevent_keydef(m);
+ default: return key_by_name_or_str(remove_escaping(token));
}
- parseError("Unknown kind '"+kind+"'", m, 1);
- return null; // Unreachable
}
- static String parseSingleQuotedString(Matcher m) throws ParseError
+ static KeyValue parse_string_keydef(Matcher m) throws ParseError
{
if (!match(m, QUOTED_PAT))
- parseError("Expected quoted string", m);
- return m.group(1).replace("\\'", "'");
+ parseError("Unterminated quoted string", m);
+ return KeyValue.makeStringKey(remove_escaping(m.group(1)));
}
- static String parsePayloadWord(Matcher m) throws ParseError
+ static KeyValue parse_keyevent_keydef(Matcher m) throws ParseError
{
if (!match(m, WORD_PAT))
- parseError("Expected a word after ':' made of [a-zA-Z0-9_]", m);
- return m.group(0);
+ parseError("Expected keyevent code", m);
+ int eventcode = 0;
+ try { eventcode = Integer.parseInt(m.group(0)); }
+ catch (Exception _e)
+ { parseError("Expected an integer payload", m); }
+ return KeyValue.keyeventKey("", eventcode, 0);
+ }
+
+ /** Returns [true] if the next token is a comma, [false] if it is the end of the input. Throws an error otherwise. */
+ static boolean parse_comma(Matcher m) throws ParseError
+ {
+ if (!match(m, KEYDEF_TOKEN))
+ return false;
+ String token = m.group(0);
+ if (!token.equals(","))
+ parseError("Expected comma instead of '"+ token + "'", m);
+ return true;
+ }
+
+ static String remove_escaping(String s)
+ {
+ if (!s.contains("\\"))
+ return s;
+ StringBuilder out = new StringBuilder(s.length());
+ final int len = s.length();
+ int prev = 0, i = 0;
+ for (; i < len; i++)
+ if (s.charAt(i) == '\\')
+ {
+ out.append(s, prev, i);
+ prev = i + 1;
+ }
+ out.append(s, prev, i);
+ return out.toString();
}
- static int parseFlags(String s, Matcher m) throws ParseError
+ /**
+ Parse a key definition starting with a [:]. This is the old syntax and is
+ kept for compatibility.
+ */
+ final static class Starting_with_colon
{
- int flags = 0;
- for (String f : s.split(","))
+ static Pattern START_PAT;
+ static Pattern ATTR_PAT;
+ static Pattern QUOTED_PAT;
+ static Pattern PAYLOAD_START_PAT;
+ static Pattern WORD_PAT;
+
+ static public KeyValue parse(String str) throws ParseError
{
- switch (f)
+ String symbol = null;
+ int flags = 0;
+ init();
+ // Kind
+ Matcher m = START_PAT.matcher(str);
+ if (!m.lookingAt())
+ parseError("Expected kind, for example \":str ...\".", m);
+ String kind = m.group(1);
+ // Attributes
+ while (true)
{
- case "dim": flags |= KeyValue.FLAG_SECONDARY; break;
- case "small": flags |= KeyValue.FLAG_SMALLER_FONT; break;
- default: parseError("Unknown flag "+f, m);
+ if (!match(m, ATTR_PAT))
+ break;
+ String attr_name = m.group(1);
+ String attr_value = parseSingleQuotedString(m);
+ switch (attr_name)
+ {
+ case "flags":
+ flags = parseFlags(attr_value, m);
+ break;
+ case "symbol":
+ symbol = attr_value;
+ break;
+
+ default:
+ parseError("Unknown attribute "+attr_name, m);
+ }
}
+ // Payload
+ if (!match(m, PAYLOAD_START_PAT))
+ parseError("Unexpected character", m);
+ String payload;
+ switch (kind)
+ {
+ case "str":
+ payload = parseSingleQuotedString(m);
+ if (symbol == null)
+ return KeyValue.makeStringKey(payload, flags);
+ return KeyValue.makeStringKeyWithSymbol(payload, symbol, flags);
+
+ case "char":
+ payload = parsePayloadWord(m);
+ if (payload.length() != 1)
+ parseError("Expected a single character payload", m);
+ return KeyValue.makeCharKey(payload.charAt(0), symbol, flags);
+
+ case "keyevent":
+ payload = parsePayloadWord(m);
+ int eventcode = 0;
+ try { eventcode = Integer.parseInt(payload); }
+ catch (Exception _e)
+ { parseError("Expected an integer payload", m); }
+ if (symbol == null)
+ symbol = String.valueOf(eventcode);
+ return KeyValue.keyeventKey(symbol, eventcode, flags);
+
+ default: break;
+ }
+ parseError("Unknown kind '"+kind+"'", m, 1);
+ return null; // Unreachable
+ }
+
+ static String parseSingleQuotedString(Matcher m) throws ParseError
+ {
+ if (!match(m, QUOTED_PAT))
+ parseError("Expected quoted string", m);
+ return m.group(1).replace("\\'", "'");
+ }
+
+ static String parsePayloadWord(Matcher m) throws ParseError
+ {
+ if (!match(m, WORD_PAT))
+ parseError("Expected a word after ':' made of [a-zA-Z0-9_]", m);
+ return m.group(0);
+ }
+
+ static int parseFlags(String s, Matcher m) throws ParseError
+ {
+ int flags = 0;
+ for (String f : s.split(","))
+ {
+ switch (f)
+ {
+ case "dim": flags |= KeyValue.FLAG_SECONDARY; break;
+ case "small": flags |= KeyValue.FLAG_SMALLER_FONT; break;
+ default: parseError("Unknown flag "+f, m);
+ }
+ }
+ return flags;
+ }
+
+ static boolean match(Matcher m, Pattern pat)
+ {
+ try { m.region(m.end(), m.regionEnd()); } catch (Exception _e) {}
+ m.usePattern(pat);
+ return m.lookingAt();
+ }
+
+ static void init()
+ {
+ if (START_PAT != null)
+ return;
+ START_PAT = Pattern.compile(":(\\w+)");
+ ATTR_PAT = Pattern.compile("\\s*(\\w+)\\s*=");
+ QUOTED_PAT = Pattern.compile("'(([^'\\\\]+|\\\\')*)'");
+ PAYLOAD_START_PAT = Pattern.compile("\\s*:");
+ WORD_PAT = Pattern.compile("[a-zA-Z0-9_]*");
}
- return flags;
}
static boolean match(Matcher m, Pattern pat)
@@ -124,17 +261,6 @@ public final class KeyValueParser
return m.lookingAt();
}
- static void init()
- {
- if (START_PAT != null)
- return;
- START_PAT = Pattern.compile(":(\\w+)");
- ATTR_PAT = Pattern.compile("\\s*(\\w+)\\s*=");
- QUOTED_PAT = Pattern.compile("'(([^'\\\\]+|\\\\')*)'");
- PAYLOAD_START_PAT = Pattern.compile("\\s*:");
- WORD_PAT = Pattern.compile("[a-zA-Z0-9_]*");
- }
-
static void parseError(String msg, Matcher m) throws ParseError
{
parseError(msg, m, m.regionStart());
@@ -145,8 +271,7 @@ public final class KeyValueParser
StringBuilder msg_ = new StringBuilder("Syntax error");
try
{
- char c = m.group(0).charAt(0);
- msg_.append(" at character '").append(c).append("'");
+ msg_.append(" at token '").append(m.group(0)).append("'");
} catch (IllegalStateException _e) {}
msg_.append(" at position ");
msg_.append(i);