2024-09-29 21:58:22 +02:00
|
|
|
package juloo.keyboard2;
|
|
|
|
|
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
/**
|
|
|
|
Parse a key definition. The syntax for a key definition is:
|
|
|
|
- [:(kind) (attributes):(payload)].
|
|
|
|
- If [str] doesn't start with a [:] character, it is interpreted as an
|
|
|
|
arbitrary string key.
|
|
|
|
|
2024-09-29 22:05:54 +02:00
|
|
|
For the different kinds and attributes, see doc/Possible-key-values.md.
|
2024-09-29 21:58:22 +02:00
|
|
|
|
|
|
|
Examples:
|
|
|
|
- [:str flags=dim,small symbol='MyKey':'My arbitrary string'].
|
|
|
|
- [:str:'My arbitrary string'].
|
|
|
|
|
|
|
|
*/
|
|
|
|
public final class KeyValueParser
|
|
|
|
{
|
|
|
|
static Pattern START_PAT;
|
|
|
|
static Pattern ATTR_PAT;
|
|
|
|
static Pattern QUOTED_PAT;
|
|
|
|
static Pattern PAYLOAD_START_PAT;
|
2024-09-29 22:05:54 +02:00
|
|
|
static Pattern SINGLE_CHAR_PAT;
|
2024-09-29 21:58:22 +02:00
|
|
|
|
|
|
|
static public KeyValue parse(String str) throws ParseError
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
switch (kind)
|
|
|
|
{
|
|
|
|
case "str":
|
2024-09-29 22:05:54 +02:00
|
|
|
String str_payload = parseSingleQuotedString(m);
|
2024-09-29 21:58:22 +02:00
|
|
|
if (symbol == null)
|
2024-09-29 22:05:54 +02:00
|
|
|
return KeyValue.makeStringKey(str_payload, flags);
|
|
|
|
return KeyValue.makeStringKeyWithSymbol(str_payload, symbol, flags);
|
2024-09-29 21:58:22 +02:00
|
|
|
|
2024-09-29 22:05:54 +02:00
|
|
|
case "char":
|
|
|
|
char char_payload = parseOneChar(m);
|
|
|
|
return KeyValue.makeCharKey(char_payload, symbol, flags);
|
2024-09-29 21:58:22 +02:00
|
|
|
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("\\'", "'");
|
|
|
|
}
|
|
|
|
|
2024-09-29 22:05:54 +02:00
|
|
|
static char parseOneChar(Matcher m) throws ParseError
|
|
|
|
{
|
|
|
|
if (!match(m, SINGLE_CHAR_PAT))
|
|
|
|
parseError("Expected a character", m);
|
|
|
|
return m.group(0).charAt(0);
|
|
|
|
}
|
|
|
|
|
2024-09-29 21:58:22 +02:00
|
|
|
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*:");
|
2024-09-29 22:05:54 +02:00
|
|
|
SINGLE_CHAR_PAT = Pattern.compile(".");
|
2024-09-29 21:58:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void parseError(String msg, Matcher m) throws ParseError
|
|
|
|
{
|
|
|
|
parseError(msg, m, m.regionStart());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parseError(String msg, Matcher m, int i) throws ParseError
|
|
|
|
{
|
|
|
|
StringBuilder msg_ = new StringBuilder("Syntax error");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
char c = m.group(0).charAt(0);
|
|
|
|
msg_.append(" at character '").append(c).append("'");
|
|
|
|
} catch (IllegalStateException _e) {}
|
|
|
|
msg_.append(" at position ");
|
|
|
|
msg_.append(i);
|
|
|
|
msg_.append(": ");
|
|
|
|
msg_.append(msg);
|
|
|
|
throw new ParseError(msg_.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class ParseError extends Exception
|
|
|
|
{
|
|
|
|
public ParseError(String msg) { super(msg); }
|
|
|
|
};
|
|
|
|
}
|