- Not quite complete. - Most of the parser is done (excluding analog) for 0.3 of the KLL spec - Transformation and correlation isn't complete yet. - Backend generation for Kiibohd capabilties is complete.simple
@@ -0,0 +1,63 @@ | |||
# Compiled source # | |||
################### | |||
*.com | |||
*.class | |||
*.dll | |||
*.exe | |||
*.o | |||
*.so | |||
*.elf | |||
*.bin | |||
*.hex | |||
*.lss | |||
*.sym | |||
*.map | |||
# Packages # | |||
############ | |||
# it's better to unpack these files and commit the raw source | |||
# git has its own built in compression methods | |||
*.7z | |||
*.dmg | |||
*.gz | |||
*.iso | |||
*.jar | |||
*.rar | |||
*.tar | |||
*.zip | |||
# Logs and databases # | |||
###################### | |||
*.log | |||
*.sql | |||
*.sqlite | |||
# OS generated files # | |||
###################### | |||
.DS_Store | |||
.DS_Store? | |||
._* | |||
.Spotlight-V100 | |||
.Trashes | |||
ehthumbs.db | |||
Thumbs.db | |||
# Editor generated files # | |||
########################## | |||
*.swp | |||
# Source browsing files # | |||
######################### | |||
tags | |||
# CMake Generated Files # | |||
######################### | |||
CMakeFiles | |||
CMakeCache.txt | |||
cmake_install.cmake | |||
# Python Generated Files # | |||
########################## | |||
__pycache__/ | |||
*.py[cod] | |||
@@ -6,6 +6,8 @@ KLL Compiler | |||
Most current version of the KLL spec: https://www.writelatex.com/read/zzqbdwqjfwwf | |||
Or visit http://kiibohd.com | |||
Uses funcparserlib: https://code.google.com/p/funcparserlib/ | |||
Usage | |||
----- |
@@ -0,0 +1,82 @@ | |||
#!/usr/bin/env python3 | |||
# KLL Compiler - Kiibohd Backend | |||
# | |||
# Backend code generator for the Kiibohd Controller firmware. | |||
# | |||
# Copyright (C) 2014 by Jacob Alexander | |||
# | |||
# This file is free software: you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | |||
# the Free Software Foundation, either version 3 of the License, or | |||
# (at your option) any later version. | |||
# | |||
# This file is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
### Imports ### | |||
import os | |||
import sys | |||
import re | |||
# Modifying Python Path, which is dumb, but the only way to import up one directory... | |||
sys.path.append( os.path.expanduser('..') ) | |||
from kll_lib.containers import * | |||
### Decorators ### | |||
## Print Decorator Variables | |||
ERROR = '\033[5;1;31mERROR\033[0m:' | |||
### Classes ### | |||
class Backend: | |||
# Initializes backend | |||
# Looks for template file and builds list of fill tags | |||
def __init__( self, templatePath ): | |||
# Does template exist? | |||
if not os.path.isfile( templatePath ): | |||
print ( "{0} '{1}' does not exist...".format( ERROR, templatePath ) ) | |||
sys.exit( 1 ) | |||
self.templatePath = templatePath | |||
self.fill_dict = dict() | |||
# Generate list of fill tags | |||
self.tagList = [] | |||
with open( templatePath, 'r' ) as openFile: | |||
for line in openFile: | |||
match = re.findall( '<\|([^|>]+)\|>', line ) | |||
for item in match: | |||
self.tagList.append( item ) | |||
# Processes content for fill tags and does any needed dataset calculations | |||
def process( self, capabilities ): | |||
## Capabilities ## | |||
self.fill_dict['CapabilitiesList'] = "const Capability CapabilitiesList[] = {\n" | |||
# Keys are pre-sorted | |||
for key in capabilities.keys(): | |||
funcName = capabilities.funcName( key ) | |||
argByteWidth = capabilities.totalArgBytes( key ) | |||
self.fill_dict['CapabilitiesList'] += "\t{{ {0}, {1} }},\n".format( funcName, argByteWidth ) | |||
self.fill_dict['CapabilitiesList'] += "};" | |||
print( self.fill_dict['CapabilitiesList'] ) | |||
# Generates the output keymap with fill tags filled | |||
def generate( self, filepath ): | |||
print("My path: {0}".format( filepath) ) | |||
@@ -0,0 +1,133 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2008/2013 Andrey Vlasovskikh | |||
# | |||
# Permission is hereby granted, free of charge, to any person obtaining | |||
# a copy of this software and associated documentation files (the | |||
# "Software"), to deal in the Software without restriction, including | |||
# without limitation the rights to use, copy, modify, merge, publish, | |||
# distribute, sublicense, and/or sell copies of the Software, and to | |||
# permit persons to whom the Software is furnished to do so, subject to | |||
# the following conditions: | |||
# | |||
# The above copyright notice and this permission notice shall be included | |||
# in all copies or substantial portions of the Software. | |||
# | |||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |||
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
__all__ = ['make_tokenizer', 'Token', 'LexerError'] | |||
import re | |||
class LexerError(Exception): | |||
def __init__(self, place, msg): | |||
self.place = place | |||
self.msg = msg | |||
def __str__(self): | |||
s = u'cannot tokenize data' | |||
line, pos = self.place | |||
return u'%s: %d,%d: "%s"' % (s, line, pos, self.msg) | |||
class Token(object): | |||
def __init__(self, type, value, start=None, end=None): | |||
self.type = type | |||
self.value = value | |||
self.start = start | |||
self.end = end | |||
def __repr__(self): | |||
return u'Token(%r, %r)' % (self.type, self.value) | |||
def __eq__(self, other): | |||
# FIXME: Case sensitivity is assumed here | |||
return self.type == other.type and self.value == other.value | |||
def _pos_str(self): | |||
if self.start is None or self.end is None: | |||
return '' | |||
else: | |||
sl, sp = self.start | |||
el, ep = self.end | |||
return u'%d,%d-%d,%d:' % (sl, sp, el, ep) | |||
def __str__(self): | |||
s = u"%s %s '%s'" % (self._pos_str(), self.type, self.value) | |||
return s.strip() | |||
@property | |||
def name(self): | |||
return self.value | |||
def pformat(self): | |||
return u"%s %s '%s'" % (self._pos_str().ljust(20), | |||
self.type.ljust(14), | |||
self.value) | |||
def make_tokenizer(specs): | |||
"""[(str, (str, int?))] -> (str -> Iterable(Token))""" | |||
def compile_spec(spec): | |||
name, args = spec | |||
return name, re.compile(*args) | |||
compiled = [compile_spec(s) for s in specs] | |||
def match_specs(specs, str, i, position): | |||
line, pos = position | |||
for type, regexp in specs: | |||
m = regexp.match(str, i) | |||
if m is not None: | |||
value = m.group() | |||
nls = value.count(u'\n') | |||
n_line = line + nls | |||
if nls == 0: | |||
n_pos = pos + len(value) | |||
else: | |||
n_pos = len(value) - value.rfind(u'\n') - 1 | |||
return Token(type, value, (line, pos + 1), (n_line, n_pos)) | |||
else: | |||
errline = str.splitlines()[line - 1] | |||
raise LexerError((line, pos + 1), errline) | |||
def f(str): | |||
length = len(str) | |||
line, pos = 1, 0 | |||
i = 0 | |||
while i < length: | |||
t = match_specs(compiled, str, i, (line, pos)) | |||
yield t | |||
line, pos = t.end | |||
i += len(t.value) | |||
return f | |||
# This is an example of a token spec. See also [this article][1] for a | |||
# discussion of searching for multiline comments using regexps (including `*?`). | |||
# | |||
# [1]: http://ostermiller.org/findcomment.html | |||
_example_token_specs = [ | |||
('COMMENT', (r'\(\*(.|[\r\n])*?\*\)', re.MULTILINE)), | |||
('COMMENT', (r'\{(.|[\r\n])*?\}', re.MULTILINE)), | |||
('COMMENT', (r'//.*',)), | |||
('NL', (r'[\r\n]+',)), | |||
('SPACE', (r'[ \t\r\n]+',)), | |||
('NAME', (r'[A-Za-z_][A-Za-z_0-9]*',)), | |||
('REAL', (r'[0-9]+\.[0-9]*([Ee][+\-]?[0-9]+)*',)), | |||
('INT', (r'[0-9]+',)), | |||
('INT', (r'\$[0-9A-Fa-f]+',)), | |||
('OP', (r'(\.\.)|(<>)|(<=)|(>=)|(:=)|[;,=\(\):\[\]\.+\-<>\*/@\^]',)), | |||
('STRING', (r"'([^']|(''))*'",)), | |||
('CHAR', (r'#[0-9]+',)), | |||
('CHAR', (r'#\$[0-9A-Fa-f]+',)), | |||
] | |||
#tokenize = make_tokenizer(_example_token_specs) |
@@ -0,0 +1,409 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2008/2013 Andrey Vlasovskikh | |||
# Small Python 3 modifications by Jacob Alexander 2014 | |||
# | |||
# Permission is hereby granted, free of charge, to any person obtaining | |||
# a copy of this software and associated documentation files (the | |||
# "Software"), to deal in the Software without restriction, including | |||
# without limitation the rights to use, copy, modify, merge, publish, | |||
# distribute, sublicense, and/or sell copies of the Software, and to | |||
# permit persons to whom the Software is furnished to do so, subject to | |||
# the following conditions: | |||
# | |||
# The above copyright notice and this permission notice shall be included | |||
# in all copies or substantial portions of the Software. | |||
# | |||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |||
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
"""A recurisve descent parser library based on functional combinators. | |||
Basic combinators are taken from Harrison's book ["Introduction to Functional | |||
Programming"][1] and translated from ML into Python. See also [a Russian | |||
translation of the book][2]. | |||
[1]: http://www.cl.cam.ac.uk/teaching/Lectures/funprog-jrh-1996/ | |||
[2]: http://code.google.com/p/funprog-ru/ | |||
A parser `p` is represented by a function of type: | |||
p :: Sequence(a), State -> (b, State) | |||
that takes as its input a sequence of tokens of arbitrary type `a` and a | |||
current parsing state and return a pair of a parsed token of arbitrary type | |||
`b` and the new parsing state. | |||
The parsing state includes the current position in the sequence being parsed and | |||
the position of the rightmost token that has been consumed while parsing. | |||
Parser functions are wrapped into an object of the class `Parser`. This class | |||
implements custom operators `+` for sequential composition of parsers, `|` for | |||
choice composition, `>>` for transforming the result of parsing. The method | |||
`Parser.parse` provides an easier way for invoking a parser hiding details | |||
related to a parser state: | |||
Parser.parse :: Parser(a, b), Sequence(a) -> b | |||
Altough this module is able to deal with a sequences of any kind of objects, the | |||
recommended way of using it is applying a parser to a `Sequence(Token)`. | |||
`Token` objects are produced by a regexp-based tokenizer defined in | |||
`funcparserlib.lexer`. By using it this way you get more readable parsing error | |||
messages (as `Token` objects contain their position in the source file) and good | |||
separation of lexical and syntactic levels of the grammar. See examples for more | |||
info. | |||
Debug messages are emitted via a `logging.Logger` object named | |||
`"funcparserlib"`. | |||
""" | |||
__all__ = [ | |||
'some', 'a', 'many', 'pure', 'finished', 'maybe', 'skip', 'oneplus', | |||
'forward_decl', 'NoParseError', | |||
] | |||
import logging | |||
log = logging.getLogger('funcparserlib') | |||
debug = False | |||
class Parser(object): | |||
"""A wrapper around a parser function that defines some operators for parser | |||
composition. | |||
""" | |||
def __init__(self, p): | |||
"""Wraps a parser function p into an object.""" | |||
self.define(p) | |||
def named(self, name): | |||
"""Specifies the name of the parser for more readable parsing log.""" | |||
self.name = name | |||
return self | |||
def define(self, p): | |||
"""Defines a parser wrapped into this object.""" | |||
f = getattr(p, 'run', p) | |||
if debug: | |||
setattr(self, '_run', f) | |||
else: | |||
setattr(self, 'run', f) | |||
self.named(getattr(p, 'name', p.__doc__)) | |||
def run(self, tokens, s): | |||
"""Sequence(a), State -> (b, State) | |||
Runs a parser wrapped into this object. | |||
""" | |||
if debug: | |||
log.debug(u'trying %s' % self.name) | |||
return self._run(tokens, s) | |||
def _run(self, tokens, s): | |||
raise NotImplementedError(u'you must define() a parser') | |||
def parse(self, tokens): | |||
"""Sequence(a) -> b | |||
Applies the parser to a sequence of tokens producing a parsing result. | |||
It provides a way to invoke a parser hiding details related to the | |||
parser state. Also it makes error messages more readable by specifying | |||
the position of the rightmost token that has been reached. | |||
""" | |||
try: | |||
(tree, _) = self.run(tokens, State()) | |||
return tree | |||
except NoParseError as e: | |||
max = e.state.max | |||
if len(tokens) > max: | |||
tok = tokens[max] | |||
else: | |||
tok = u'<EOF>' | |||
raise NoParseError(u'%s: %s' % (e.msg, tok), e.state) | |||
def __add__(self, other): | |||
"""Parser(a, b), Parser(a, c) -> Parser(a, _Tuple(b, c)) | |||
A sequential composition of parsers. | |||
NOTE: The real type of the parsed value isn't always such as specified. | |||
Here we use dynamic typing for ignoring the tokens that are of no | |||
interest to the user. Also we merge parsing results into a single _Tuple | |||
unless the user explicitely prevents it. See also skip and >> | |||
combinators. | |||
""" | |||
def magic(v1, v2): | |||
vs = [v for v in [v1, v2] if not isinstance(v, _Ignored)] | |||
if len(vs) == 1: | |||
return vs[0] | |||
elif len(vs) == 2: | |||
if isinstance(vs[0], _Tuple): | |||
return _Tuple(v1 + (v2,)) | |||
else: | |||
return _Tuple(vs) | |||
else: | |||
return _Ignored(()) | |||
@Parser | |||
def _add(tokens, s): | |||
(v1, s2) = self.run(tokens, s) | |||
(v2, s3) = other.run(tokens, s2) | |||
return magic(v1, v2), s3 | |||
# or in terms of bind and pure: | |||
# _add = self.bind(lambda x: other.bind(lambda y: pure(magic(x, y)))) | |||
_add.name = u'(%s , %s)' % (self.name, other.name) | |||
return _add | |||
def __or__(self, other): | |||
"""Parser(a, b), Parser(a, c) -> Parser(a, b or c) | |||
A choice composition of two parsers. | |||
NOTE: Here we are not providing the exact type of the result. In a | |||
statically typed langage something like Either b c could be used. See | |||
also + combinator. | |||
""" | |||
@Parser | |||
def _or(tokens, s): | |||
try: | |||
return self.run(tokens, s) | |||
except NoParseError as e: | |||
return other.run(tokens, State(s.pos, e.state.max)) | |||
_or.name = u'(%s | %s)' % (self.name, other.name) | |||
return _or | |||
def __rshift__(self, f): | |||
"""Parser(a, b), (b -> c) -> Parser(a, c) | |||
Given a function from b to c, transforms a parser of b into a parser of | |||
c. It is useful for transorming a parser value into another value for | |||
making it a part of a parse tree or an AST. | |||
This combinator may be thought of as a functor from b -> c to Parser(a, | |||
b) -> Parser(a, c). | |||
""" | |||
@Parser | |||
def _shift(tokens, s): | |||
(v, s2) = self.run(tokens, s) | |||
return f(v), s2 | |||
# or in terms of bind and pure: | |||
# _shift = self.bind(lambda x: pure(f(x))) | |||
_shift.name = u'(%s)' % (self.name,) | |||
return _shift | |||
def bind(self, f): | |||
"""Parser(a, b), (b -> Parser(a, c)) -> Parser(a, c) | |||
NOTE: A monadic bind function. It is used internally to implement other | |||
combinators. Functions bind and pure make the Parser a Monad. | |||
""" | |||
@Parser | |||
def _bind(tokens, s): | |||
(v, s2) = self.run(tokens, s) | |||
return f(v).run(tokens, s2) | |||
_bind.name = u'(%s >>=)' % (self.name,) | |||
return _bind | |||
class State(object): | |||
"""A parsing state that is maintained basically for error reporting. | |||
It consists of the current position pos in the sequence being parsed and | |||
the position max of the rightmost token that has been consumed while | |||
parsing. | |||
""" | |||
def __init__(self, pos=0, max=0): | |||
self.pos = pos | |||
self.max = max | |||
def __str__(self): | |||
return unicode((self.pos, self.max)) | |||
def __repr__(self): | |||
return u'State(%r, %r)' % (self.pos, self.max) | |||
class NoParseError(Exception): | |||
def __init__(self, msg=u'', state=None): | |||
self.msg = msg | |||
self.state = state | |||
def __str__(self): | |||
return self.msg | |||
class _Tuple(tuple): | |||
pass | |||
class _Ignored(object): | |||
def __init__(self, value): | |||
self.value = value | |||
def __repr__(self): | |||
return u'_Ignored(%s)' % repr(self.value) | |||
@Parser | |||
def finished(tokens, s): | |||
"""Parser(a, None) | |||
Throws an exception if any tokens are left in the input unparsed. | |||
""" | |||
if s.pos >= len(tokens): | |||
return None, s | |||
else: | |||
raise NoParseError(u'should have reached <EOF>', s) | |||
finished.name = u'finished' | |||
def many(p): | |||
"""Parser(a, b) -> Parser(a, [b]) | |||
Returns a parser that infinitely applies the parser p to the input sequence | |||
of tokens while it successfully parsers them. The resulting parser returns a | |||
list of parsed values. | |||
""" | |||
@Parser | |||
def _many(tokens, s): | |||
"""Iterative implementation preventing the stack overflow.""" | |||
res = [] | |||
try: | |||
while True: | |||
(v, s) = p.run(tokens, s) | |||
res.append(v) | |||
except NoParseError as e: | |||
return res, State(s.pos, e.state.max) | |||
_many.name = u'{ %s }' % p.name | |||
return _many | |||
def some(pred): | |||
"""(a -> bool) -> Parser(a, a) | |||
Returns a parser that parses a token if it satisfies a predicate pred. | |||
""" | |||
@Parser | |||
def _some(tokens, s): | |||
if s.pos >= len(tokens): | |||
raise NoParseError(u'no tokens left in the stream', s) | |||
else: | |||
t = tokens[s.pos] | |||
if pred(t): | |||
pos = s.pos + 1 | |||
s2 = State(pos, max(pos, s.max)) | |||
if debug: | |||
log.debug(u'*matched* "%s", new state = %s' % (t, s2)) | |||
return t, s2 | |||
else: | |||
if debug: | |||
log.debug(u'failed "%s", state = %s' % (t, s)) | |||
raise NoParseError(u'got unexpected token', s) | |||
_some.name = u'(some)' | |||
return _some | |||
def a(value): | |||
"""Eq(a) -> Parser(a, a) | |||
Returns a parser that parses a token that is equal to the value value. | |||
""" | |||
name = getattr(value, 'name', value) | |||
return some(lambda t: t == value).named(u'(a "%s")' % (name,)) | |||
def pure(x): | |||
@Parser | |||
def _pure(_, s): | |||
return x, s | |||
_pure.name = u'(pure %r)' % (x,) | |||
return _pure | |||
def maybe(p): | |||
"""Parser(a, b) -> Parser(a, b or None) | |||
Returns a parser that retuns None if parsing fails. | |||
NOTE: In a statically typed language, the type Maybe b could be more | |||
approprieate. | |||
""" | |||
return (p | pure(None)).named(u'[ %s ]' % (p.name,)) | |||
def skip(p): | |||
"""Parser(a, b) -> Parser(a, _Ignored(b)) | |||
Returns a parser which results are ignored by the combinator +. It is useful | |||
for throwing away elements of concrete syntax (e. g. ",", ";"). | |||
""" | |||
return p >> _Ignored | |||
def oneplus(p): | |||
"""Parser(a, b) -> Parser(a, [b]) | |||
Returns a parser that applies the parser p one or more times. | |||
""" | |||
q = p + many(p) >> (lambda x: [x[0]] + x[1]) | |||
return q.named(u'(%s , { %s })' % (p.name, p.name)) | |||
def with_forward_decls(suspension): | |||
"""(None -> Parser(a, b)) -> Parser(a, b) | |||
Returns a parser that computes itself lazily as a result of the suspension | |||
provided. It is needed when some parsers contain forward references to | |||
parsers defined later and such references are cyclic. See examples for more | |||
details. | |||
""" | |||
@Parser | |||
def f(tokens, s): | |||
return suspension().run(tokens, s) | |||
return f | |||
def forward_decl(): | |||
"""None -> Parser(?, ?) | |||
Returns an undefined parser that can be used as a forward declaration. You | |||
will be able to define() it when all the parsers it depends on are | |||
available. | |||
""" | |||
@Parser | |||
def f(tokens, s): | |||
raise NotImplementedError(u'you must define() a forward_decl somewhere') | |||
return f | |||
if __name__ == '__main__': | |||
import doctest | |||
doctest.testmod() |
@@ -0,0 +1,49 @@ | |||
# -*- coding: utf-8 -*- | |||
# Copyright (c) 2008/2013 Andrey Vlasovskikh | |||
# | |||
# Permission is hereby granted, free of charge, to any person obtaining | |||
# a copy of this software and associated documentation files (the | |||
# "Software"), to deal in the Software without restriction, including | |||
# without limitation the rights to use, copy, modify, merge, publish, | |||
# distribute, sublicense, and/or sell copies of the Software, and to | |||
# permit persons to whom the Software is furnished to do so, subject to | |||
# the following conditions: | |||
# | |||
# The above copyright notice and this permission notice shall be included | |||
# in all copies or substantial portions of the Software. | |||
# | |||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |||
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
def pretty_tree(x, kids, show): | |||
"""(a, (a -> list(a)), (a -> str)) -> str | |||
Returns a pseudographic tree representation of x similar to the tree command | |||
in Unix. | |||
""" | |||
(MID, END, CONT, LAST, ROOT) = (u'|-- ', u'`-- ', u'| ', u' ', u'') | |||
def rec(x, indent, sym): | |||
line = indent + sym + show(x) | |||
xs = kids(x) | |||
if len(xs) == 0: | |||
return line | |||
else: | |||
if sym == MID: | |||
next_indent = indent + CONT | |||
elif sym == ROOT: | |||
next_indent = indent + ROOT | |||
else: | |||
next_indent = indent + LAST | |||
syms = [MID] * (len(xs) - 1) + [END] | |||
lines = [rec(x, next_indent, sym) for x, sym in zip(xs, syms)] | |||
return u'\n'.join([line] + lines) | |||
return rec(x, u'', ROOT) |
@@ -0,0 +1,505 @@ | |||
#!/usr/bin/env python3 | |||
# KLL Compiler | |||
# Keyboard Layout Langauge | |||
# | |||
# Copyright (C) 2014 by Jacob Alexander | |||
# | |||
# This file is free software: you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | |||
# the Free Software Foundation, either version 3 of the License, or | |||
# (at your option) any later version. | |||
# | |||
# This file is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
### Imports ### | |||
import argparse | |||
import io | |||
import os | |||
import re | |||
import sys | |||
import token | |||
import importlib | |||
from tokenize import generate_tokens | |||
from re import VERBOSE | |||
from pprint import pformat | |||
from kll_lib.hid_dict import * | |||
from kll_lib.containers import * | |||
from funcparserlib.lexer import make_tokenizer, Token, LexerError | |||
from funcparserlib.parser import (some, a, many, oneplus, skip, finished, maybe, skip, forward_decl, NoParseError) | |||
### Decorators ### | |||
## Print Decorator Variables | |||
ERROR = '\033[5;1;31mERROR\033[0m:' | |||
## Python Text Formatting Fixer... | |||
## Because the creators of Python are averse to proper capitalization. | |||
textFormatter_lookup = { | |||
"usage: " : "Usage: ", | |||
"optional arguments" : "Optional Arguments", | |||
} | |||
def textFormatter_gettext( s ): | |||
return textFormatter_lookup.get( s, s ) | |||
argparse._ = textFormatter_gettext | |||
### Argument Parsing ### | |||
def processCommandLineArgs(): | |||
# Setup argument processor | |||
pArgs = argparse.ArgumentParser( | |||
usage="%(prog)s [options] <file1>...", | |||
description="Generates .h file state tables and pointer indices from KLL .kll files.", | |||
epilog="Example: {0} TODO".format( os.path.basename( sys.argv[0] ) ), | |||
formatter_class=argparse.RawTextHelpFormatter, | |||
add_help=False, | |||
) | |||
# Positional Arguments | |||
pArgs.add_argument( 'files', type=str, nargs='+', | |||
help=argparse.SUPPRESS ) # Suppressed help output, because Python output is verbosely ugly | |||
# Optional Arguments | |||
pArgs.add_argument( '-b', '--backend', type=str, default="kiibohd", | |||
help="Specify target backend for the KLL compiler.\n" | |||
"Default: kiibohd" ) | |||
pArgs.add_argument( '-p', '--partial', type=str, nargs='+', action='append', | |||
help="Specify .kll files to generate partial map, multiple files per flag.\n" | |||
"Each -p defines another partial map.\n" | |||
"Base .kll files (that define the scan code maps) must be defined for each partial map." ) | |||
pArgs.add_argument( '-t', '--template', type=str, default="templateKeymap.h", | |||
help="Specify template used to generate the keymap.\n" | |||
"Default: templateKeymap.h" ) | |||
pArgs.add_argument( '-o', '--output', type=str, default="templateKeymap.h", | |||
help="Specify output file. Writes to current working directory by default.\n" | |||
"Default: generatedKeymap.h" ) | |||
pArgs.add_argument( '-h', '--help', action="help", | |||
help="This message." ) | |||
# Process Arguments | |||
args = pArgs.parse_args() | |||
# Parameters | |||
defaultFiles = args.files | |||
partialFileSets = args.partial | |||
if partialFileSets is None: | |||
partialFileSets = [[]] | |||
# Check file existance | |||
for filename in defaultFiles: | |||
if not os.path.isfile( filename ): | |||
print ( "{0} {1} does not exist...".format( ERROR, filename ) ) | |||
sys.exit( 1 ) | |||
for partial in partialFileSets: | |||
for filename in partial: | |||
if not os.path.isfile( filename ): | |||
print ( "{0} {1} does not exist...".format( ERROR, filename ) ) | |||
sys.exit( 1 ) | |||
return (defaultFiles, partialFileSets, args.backend, args.template, args.output) | |||
### Tokenizer ### | |||
def tokenize( string ): | |||
"""str -> Sequence(Token)""" | |||
# Basic Tokens Spec | |||
specs = [ | |||
( 'Comment', ( r' *#.*', ) ), | |||
( 'Space', ( r'[ \t\r\n]+', ) ), | |||
( 'USBCode', ( r'U(("[^"]+")|(0x[0-9a-fA-F]+)|([0-9]+))', ) ), | |||
( 'USBCodeStart', ( r'U\[', ) ), | |||
( 'ScanCode', ( r'S((0x[0-9a-fA-F]+)|([0-9]+))', ) ), | |||
( 'ScanCodeStart', ( r'S\[', ) ), | |||
( 'CodeEnd', ( r'\]', ) ), | |||
( 'String', ( r'"[^"]*"', VERBOSE ) ), | |||
( 'SequenceString', ( r"'[^']*'", ) ), | |||
( 'Comma', ( r',', ) ), | |||
( 'Dash', ( r'-', ) ), | |||
( 'Plus', ( r'\+', ) ), | |||
( 'Operator', ( r'=>|:|=', ) ), | |||
( 'Parenthesis', ( r'\(|\)', ) ), | |||
( 'Number', ( r'-?(0x[0-9a-fA-F]+)|(0|([1-9][0-9]*))', VERBOSE ) ), | |||
( 'Name', ( r'[A-Za-z_][A-Za-z_0-9]*', ) ), | |||
( 'VariableContents', ( r'''[^"' ;:=>()]+''', ) ), | |||
( 'EndOfLine', ( r';', ) ), | |||
] | |||
# Tokens to filter out of the token stream | |||
useless = ['Space', 'Comment'] | |||
tokens = make_tokenizer( specs ) | |||
return [x for x in tokens( string ) if x.type not in useless] | |||
### Parsing ### | |||
## Map Arrays | |||
scanCode_map = [ None ] * 0xFF # Define 8 bit address width | |||
usbCode_map = [ None ] * 0xFF # Define 8 bit address width | |||
variable_dict = dict() | |||
capabilities_dict = Capabilities() | |||
## Parsing Functions | |||
def make_scanCode( token ): | |||
scanCode = int( token[1:], 0 ) | |||
# Check size, to make sure it's valid | |||
if scanCode > 0xFF: | |||
print ( "{0} ScanCode value {1} is larger than 255".format( ERROR, scanCode ) ) | |||
raise | |||
return scanCode | |||
def make_usbCode( token ): | |||
# If first character is a U, strip | |||
if token[0] == "U": | |||
token = token[1:] | |||
# If using string representation of USB Code, do lookup, case-insensitive | |||
if '"' in token: | |||
try: | |||
usbCode = kll_hid_lookup_dictionary[ token[1:-1].upper() ] | |||
except LookupError as err: | |||
print ( "{0} {1} is an invalid USB Code Lookup...".format( ERROR, err ) ) | |||
raise | |||
else: | |||
usbCode = int( token, 0 ) | |||
# Check size, to make sure it's valid | |||
if usbCode > 0xFF: | |||
print ( "{0} USBCode value {1} is larger than 255".format( ERROR, usbCode ) ) | |||
raise | |||
return usbCode | |||
def make_seqString( token ): | |||
# Shifted Characters, and amount to move by to get non-shifted version | |||
# US ANSI | |||
shiftCharacters = ( | |||
( "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0x20 ), | |||
( "+", 0x12 ), | |||
( "&(", 0x11 ), | |||
( "!#$%<>", 0x10 ), | |||
( "*", 0x0E ), | |||
( ")", 0x07 ), | |||
( '"', 0x05 ), | |||
( ":", 0x01 ), | |||
( "^", -0x10 ), | |||
( "_", -0x18 ), | |||
( "{}|", -0x1E ), | |||
( "~", -0x20 ), | |||
( "@", -0x32 ), | |||
( "?", -0x38 ), | |||
) | |||
listOfLists = [] | |||
shiftKey = kll_hid_lookup_dictionary["SHIFT"] | |||
# Creates a list of USB codes from the string: sequence (list) of combos (lists) | |||
for char in token[1:-1]: | |||
processedChar = char | |||
# Whether or not to create a combo for this sequence with a shift | |||
shiftCombo = False | |||
# Depending on the ASCII character, convert to single character or Shift + character | |||
for pair in shiftCharacters: | |||
if char in pair[0]: | |||
shiftCombo = True | |||
processedChar = chr( ord( char ) + pair[1] ) | |||
break | |||
# Do KLL HID Lookup on non-shifted character | |||
# NOTE: Case-insensitive, which is why the shift must be pre-computed | |||
usbCode = kll_hid_lookup_dictionary[ processedChar.upper() ] | |||
# Create Combo for this character, add shift key if shifted | |||
charCombo = [] | |||
if shiftCombo: | |||
charCombo = [ [ shiftKey ] ] | |||
charCombo.append( [ usbCode ] ) | |||
# Add to list of lists | |||
listOfLists.append( charCombo ) | |||
return listOfLists | |||
def make_string( token ): | |||
return token[1:-1] | |||
def make_number( token ): | |||
return int( token, 0 ) | |||
# Range can go from high to low or low to high | |||
def make_scanCode_range( rangeVals ): | |||
start = rangeVals[0] | |||
end = rangeVals[1] | |||
# Swap start, end if start is greater than end | |||
if start > end: | |||
start, end = end, start | |||
# Iterate from start to end, and generate the range | |||
return list( range( start, end + 1 ) ) | |||
# Range can go from high to low or low to high | |||
# Warn on 0-9 (as this does not do what one would expect) TODO | |||
# Lookup USB HID tags and convert to a number | |||
def make_usbCode_range( rangeVals ): | |||
# Check if already integers | |||
if isinstance( rangeVals[0], int ): | |||
start = rangeVals[0] | |||
else: | |||
start = make_usbCode( rangeVals[0] ) | |||
if isinstance( rangeVals[1], int ): | |||
end = rangeVals[1] | |||
else: | |||
end = make_usbCode( rangeVals[1] ) | |||
# Swap start, end if start is greater than end | |||
if start > end: | |||
start, end = end, start | |||
# Iterate from start to end, and generate the range | |||
return list( range( start, end + 1 ) ) | |||
pass | |||
## Base Rules | |||
const = lambda x: lambda _: x | |||
unarg = lambda f: lambda x: f(*x) | |||
flatten = lambda list: sum( list, [] ) | |||
tokenValue = lambda x: x.value | |||
tokenType = lambda t: some( lambda x: x.type == t ) >> tokenValue | |||
operator = lambda s: a( Token( 'Operator', s ) ) >> tokenValue | |||
parenthesis = lambda s: a( Token( 'Parenthesis', s ) ) >> tokenValue | |||
eol = a( Token( 'EndOfLine', ';' ) ) | |||
def listElem( item ): | |||
return [ item ] | |||
# Flatten only the top layer (list of lists of ...) | |||
def oneLayerFlatten( items ): | |||
mainList = [] | |||
for sublist in items: | |||
for item in sublist: | |||
mainList.append( item ) | |||
return mainList | |||
# Expand ranges of values in the 3rd dimension of the list, to a list of 2nd lists | |||
# i.e. [ sequence, [ combo, [ range ] ] ] --> [ [ sequence, [ combo ] ], <option 2>, <option 3> ] | |||
def optionExpansion( sequences ): | |||
expandedSequences = [] | |||
# Total number of combinations of the sequence of combos that needs to be generated | |||
totalCombinations = 1 | |||
# List of leaf lists, with number of leaves | |||
maxLeafList = [] | |||
# Traverse to the leaf nodes, and count the items in each leaf list | |||
for sequence in sequences: | |||
for combo in sequence: | |||
rangeLen = len( combo ) | |||
totalCombinations *= rangeLen | |||
maxLeafList.append( rangeLen ) | |||
# Counter list to keep track of which combination is being generated | |||
curLeafList = [0] * len( maxLeafList ) | |||
# Generate a list of permuations of the sequence of combos | |||
for count in range( 0, totalCombinations ): | |||
expandedSequences.append( [] ) # Prepare list for adding the new combination | |||
position = 0 | |||
# Traverse sequence of combos to generate permuation | |||
for sequence in sequences: | |||
expandedSequences[ -1 ].append( [] ) | |||
for combo in sequence: | |||
expandedSequences[ -1 ][ -1 ].append( combo[ curLeafList[ position ] ] ) | |||
position += 1 | |||
# Increment combination tracker | |||
for leaf in range( 0, len( curLeafList ) ): | |||
curLeafList[ leaf ] += 1 | |||
# Reset this position, increment next position (if it exists), then stop | |||
if curLeafList[ leaf ] >= maxLeafList[ leaf ]: | |||
curLeafList[ leaf ] = 0 | |||
if leaf + 1 < len( curLeafList ): | |||
curLeafList[ leaf + 1 ] += 1 | |||
break | |||
return expandedSequences | |||
## Evaluation Rules | |||
def eval_scanCode( trigger, result ): | |||
# Convert to lists of lists of lists to tuples of tuples of tuples | |||
trigger = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in trigger ) | |||
result = tuple( tuple( tuple( sequence ) for sequence in variant ) for variant in result ) | |||
# Add to the base scanCode map, overwrite if already defined | |||
# if scanCode_map[ trigger ] != None: | |||
# print ( "ScanCodeMap - Replacing '{0}' with '{1}' -> {2}".format( scanCode_map[ trigger ], result, trigger ) ) | |||
# scanCode_map[ trigger ] = result | |||
def eval_usbCode( trigger, result ): | |||
# Check if trigger is list | |||
# Add to the base usbCode map, overwrite if already defined | |||
if usbCode_map[ trigger ] != None: | |||
print ( "USBCodeMap - Replacing '{0}' with '{1}' -> {2}".format( usbCode_map[ trigger ], result, trigger ) ) | |||
usbCode_map[ trigger ] = result | |||
print ( trigger ) | |||
def eval_variable( name, content ): | |||
# Content might be a concatenation of multiple data types, convert everything into a single string | |||
assigned_content = "" | |||
for item in content: | |||
assigned_content += str( item ) | |||
variable_dict[ name ] = assigned_content | |||
def eval_capability( name, function, args ): | |||
capabilities_dict[ name ] = [ function, args ] | |||
map_scanCode = unarg( eval_scanCode ) | |||
map_usbCode = unarg( eval_usbCode ) | |||
set_variable = unarg( eval_variable ) | |||
set_capability = unarg( eval_capability ) | |||
## Sub Rules | |||
usbCode = tokenType('USBCode') >> make_usbCode | |||
scanCode = tokenType('ScanCode') >> make_scanCode | |||
name = tokenType('Name') | |||
number = tokenType('Number') >> make_number | |||
comma = tokenType('Comma') | |||
dash = tokenType('Dash') | |||
plus = tokenType('Plus') | |||
content = tokenType('VariableContents') | |||
string = tokenType('String') >> make_string | |||
unString = tokenType('String') # When the double quotes are still needed for internal processing | |||
seqString = tokenType('SequenceString') >> make_seqString | |||
# Code variants | |||
code_end = tokenType('CodeEnd') | |||
# Scan Codes | |||
scanCode_start = tokenType('ScanCodeStart') | |||
scanCode_range = number + skip( dash ) + number >> make_scanCode_range | |||
scanCode_listElem = number >> listElem | |||
scanCode_innerList = oneplus( ( scanCode_range | scanCode_listElem ) + skip( maybe( comma ) ) ) >> flatten | |||
scanCode_expanded = skip( scanCode_start ) + scanCode_innerList + skip( code_end ) | |||
scanCode_elem = scanCode >> listElem | |||
scanCode_combo = oneplus( ( scanCode_expanded | scanCode_elem ) + skip( maybe( plus ) ) ) | |||
scanCode_sequence = oneplus( scanCode_combo + skip( maybe( comma ) ) ) | |||
# USB Codes | |||
usbCode_start = tokenType('USBCodeStart') | |||
usbCode_range = ( number | unString ) + skip( dash ) + ( number | unString ) >> make_usbCode_range | |||
usbCode_listElemTag = unString >> make_usbCode | |||
usbCode_listElem = ( number | usbCode_listElemTag ) >> listElem | |||
usbCode_innerList = oneplus( ( usbCode_range | usbCode_listElem ) + skip( maybe( comma ) ) ) >> flatten | |||
usbCode_expanded = skip( usbCode_start ) + usbCode_innerList + skip( code_end ) | |||
usbCode_elem = usbCode >> listElem | |||
usbCode_combo = oneplus( ( usbCode_expanded | usbCode_elem ) + skip( maybe( plus ) ) ) >> listElem | |||
usbCode_sequence = oneplus( ( usbCode_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten | |||
# Capabilities | |||
capFunc_arguments = number + skip( maybe( comma ) ) | |||
capFunc_elem = name + skip( parenthesis('(') ) + many( capFunc_arguments ) + skip( parenthesis(')') ) >> listElem | |||
capFunc_combo = oneplus( ( usbCode_expanded | usbCode_elem | capFunc_elem ) + skip( maybe( plus ) ) ) >> listElem | |||
capFunc_sequence = oneplus( ( capFunc_combo | seqString ) + skip( maybe( comma ) ) ) >> oneLayerFlatten | |||
# Trigger / Result Codes | |||
triggerCode_outerList = scanCode_sequence >> optionExpansion | |||
triggerUSBCode_outerList = usbCode_sequence >> optionExpansion | |||
resultCode_outerList = capFunc_sequence >> optionExpansion | |||
## Main Rules | |||
#| <variable> = <variable contents>; | |||
variable_contents = name | content | string | number | comma | dash | |||
variable_expression = name + skip( operator('=') ) + oneplus( variable_contents ) + skip( eol ) >> set_variable | |||
#| <capability name> => <c function>; | |||
capability_arguments = name + skip( operator(':') ) + number + skip( maybe( comma ) ) | |||
capability_expression = name + skip( operator('=>') ) + name + skip( parenthesis('(') ) + many( capability_arguments ) + skip( parenthesis(')') ) + skip( eol ) >> set_capability | |||
#| <trigger> : <result>; | |||
scanCode_expression = triggerCode_outerList + skip( operator(':') ) + resultCode_outerList + skip( eol ) >> map_scanCode | |||
usbCode_expression = triggerUSBCode_outerList + skip( operator(':') ) + resultCode_outerList + skip( eol ) #>> map_usbCode | |||
def parse( tokenSequence ): | |||
"""Sequence(Token) -> object""" | |||
# Top-level Parser | |||
expression = scanCode_expression | usbCode_expression | variable_expression | capability_expression | |||
kll_text = many( expression ) | |||
kll_file = maybe( kll_text ) + skip( finished ) | |||
return kll_file.parse( tokenSequence ) | |||
### Main Entry Point ### | |||
if __name__ == '__main__': | |||
(defaultFiles, partialFileSets, backend_name, template, output) = processCommandLineArgs() | |||
# Load backend module | |||
global backend | |||
backend_import = importlib.import_module( "backends.{0}".format( backend_name ) ) | |||
backend = backend_import.Backend( template ) | |||
#TODO Move elsewhere | |||
for filename in defaultFiles: | |||
with open( filename ) as file: | |||
data = file.read() | |||
tokenSequence = tokenize( data ) | |||
print ( pformat( tokenSequence ) ) | |||
tree = parse( tokenSequence ) | |||
#print ( tree ) | |||
#print ( scanCode_map ) | |||
#print ( usbCode_map ) | |||
print ( variable_dict ) | |||
print ( capabilities_dict ) | |||
# TODO Move | |||
backend.process( capabilities_dict ) | |||
# Successful Execution | |||
sys.exit( 0 ) | |||
@@ -0,0 +1,114 @@ | |||
#!/usr/bin/env python3 | |||
# KLL Compiler Containers | |||
# | |||
# Copyright (C) 2014 by Jacob Alexander | |||
# | |||
# This file is free software: you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | |||
# the Free Software Foundation, either version 3 of the License, or | |||
# (at your option) any later version. | |||
# | |||
# This file is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
### Imports ### | |||
### Decorators ### | |||
## Print Decorator Variables | |||
ERROR = '\033[5;1;31mERROR\033[0m:' | |||
### Parsing ### | |||
## Containers | |||
class Capabilities: | |||
# Container for capabilities dictionary and convenience functions | |||
def __init__( self ): | |||
self.capabilities = dict() | |||
def __getitem__( self, name ): | |||
return self.capabilities[ name ] | |||
def __setitem__( self, name, contents ): | |||
self.capabilities[ name ] = contents | |||
def __repr__( self ): | |||
return "Capabilities => {0}\nIndexed Capabilities => {1}".format( self.capabilities, sorted( self.capabilities, key = self.capabilities.get ) ) | |||
# Total bytes needed to store arguments | |||
def totalArgBytes( self, name ): | |||
totalBytes = 0 | |||
# Iterate over the arguments, summing the total bytes | |||
for arg in self.capabilities[ name ][1]: | |||
totalBytes += int( arg[1] ) | |||
return totalBytes | |||
# Name of the capability function | |||
def funcName( self, name ): | |||
return self.capabilities[ name ][0] | |||
# Only valid while dictionary keys are not added/removed | |||
def getIndex( self, name ): | |||
return sorted( self.capabilities, key = self.capabilities.get ).index( name ) | |||
def getName( self, index ): | |||
return sorted( self.capabilities, key = self.capabilities.get )[ index ] | |||
def keys( self ): | |||
return sorted( self.capabilities, key = self.capabilities.get ) | |||
class Macros: | |||
# Container for Trigger Macro : Result Macro correlation | |||
# Layer selection for generating TriggerLists | |||
# | |||
# Only convert USB Code list once all the ResultMacros have been accumulated (does a macro reduction; not reversible) | |||
# Two staged list for ResultMacros: | |||
# 1) USB Code/Non-converted (may contain capabilities) | |||
# 2) Capabilities | |||
def __init__( self ): | |||
# Default layer (0) | |||
self.layer = 0 | |||
# Macro Storage | |||
self.macros = [ [] ] | |||
def setLayer( self, layer ): | |||
self.layer = layer | |||
# Use for ScanCode trigger macros | |||
def appendScanCode( self, trigger, result ): | |||
self.macros[ self.layer ][ trigger ] = result | |||
# Use for USBCode trigger macros | |||
# An extra lookup is required | |||
def appendUSBCode( self, trigger, result ): | |||
noSuccess = True | |||
for macro in self.macros[ self.layer ].keys(): | |||
# USB Code Found | |||
if trigger == self.macros[ self.layer ][ macro ]: | |||
print ( "USBCode - Replacing '{0}' with '{1}' -> '{2}'".format( trigger, macro, result ) ) | |||
self.macros[ self.layer ][ macro ] = result | |||
noSuccess = False | |||
# Only show warning if no replacements were done | |||
if noSuccess: | |||
print ( "Warning: '{1}' USB Code not found in layer {1}".format( trigger, self.layer ) ) | |||
return False | |||
return True | |||
@@ -0,0 +1,502 @@ | |||
#!/usr/bin/env python3 | |||
# KLL Compiler - HID Dictionary Lookup | |||
# | |||
# USB Code Lookup Dictionary | |||
# | |||
# Copyright (C) 2014 by Jacob Alexander | |||
# | |||
# This file is free software: you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | |||
# the Free Software Foundation, either version 3 of the License, or | |||
# (at your option) any later version. | |||
# | |||
# This file is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# You should have received a copy of the GNU General Public License | |||
# along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
# Rather than generating tables of hex USB codes for the keymapping tables, readable defines are used (which correspond to usb_hid.h) | |||
hid_lookup_dictionary = dict([ | |||
( 0x00, 'KEY_NOEVENT' ), # Event, not a physical key | |||
( 0x01, 'KEY_ERRORROLLOVER' ), # Event, not a physical key | |||
( 0x02, 'KEY_POSTFAIL' ), # Event, not a physical key | |||
( 0x03, 'KEY_ERRORUNDEFINED' ), # Event, not a physical key | |||
( 0x04, 'KEY_A' ), | |||
( 0x05, 'KEY_B' ), | |||
( 0x06, 'KEY_C' ), | |||
( 0x07, 'KEY_D' ), | |||
( 0x08, 'KEY_E' ), | |||
( 0x09, 'KEY_F' ), | |||
( 0x0A, 'KEY_G' ), | |||
( 0x0B, 'KEY_H' ), | |||
( 0x0C, 'KEY_I' ), | |||
( 0x0D, 'KEY_J' ), | |||
( 0x0E, 'KEY_K' ), | |||
( 0x0F, 'KEY_L' ), | |||
( 0x10, 'KEY_M' ), | |||
( 0x11, 'KEY_N' ), | |||
( 0x12, 'KEY_O' ), | |||
( 0x13, 'KEY_P' ), | |||
( 0x14, 'KEY_Q' ), | |||
( 0x15, 'KEY_R' ), | |||
( 0x16, 'KEY_S' ), | |||
( 0x17, 'KEY_T' ), | |||
( 0x18, 'KEY_U' ), | |||
( 0x19, 'KEY_V' ), | |||
( 0x1A, 'KEY_W' ), | |||
( 0x1B, 'KEY_X' ), | |||
( 0x1C, 'KEY_Y' ), | |||
( 0x1D, 'KEY_Z' ), | |||
( 0x1E, 'KEY_1' ), | |||
( 0x1F, 'KEY_2' ), | |||
( 0x20, 'KEY_3' ), | |||
( 0x21, 'KEY_4' ), | |||
( 0x22, 'KEY_5' ), | |||
( 0x23, 'KEY_6' ), | |||
( 0x24, 'KEY_7' ), | |||
( 0x25, 'KEY_8' ), | |||
( 0x26, 'KEY_9' ), | |||
( 0x27, 'KEY_0' ), | |||
( 0x28, 'KEY_ENTER' ), | |||
( 0x29, 'KEY_ESC' ), | |||
( 0x2A, 'KEY_BACKSPACE' ), | |||
( 0x2B, 'KEY_TAB' ), | |||
( 0x2C, 'KEY_SPACE' ), | |||
( 0x2D, 'KEY_MINUS' ), | |||
( 0x2E, 'KEY_EQUAL' ), | |||
( 0x2F, 'KEY_LEFT_BRACE' ), | |||
( 0x30, 'KEY_RIGHT_BRACE' ), | |||
( 0x31, 'KEY_BACKSLASH' ), | |||
( 0x32, 'KEY_NUMBER' ), | |||
( 0x33, 'KEY_SEMICOLON' ), | |||
( 0x34, 'KEY_QUOTE' ), | |||
( 0x35, 'KEY_BACKTICK' ), | |||
( 0x36, 'KEY_COMMA' ), | |||
( 0x37, 'KEY_PERIOD' ), | |||
( 0x38, 'KEY_SLASH' ), | |||
( 0x39, 'KEY_CAPS_LOCK' ), | |||
( 0x3A, 'KEY_F1' ), | |||
( 0x3B, 'KEY_F2' ), | |||
( 0x3C, 'KEY_F3' ), | |||
( 0x3D, 'KEY_F4' ), | |||
( 0x3E, 'KEY_F5' ), | |||
( 0x3F, 'KEY_F6' ), | |||
( 0x40, 'KEY_F7' ), | |||
( 0x41, 'KEY_F8' ), | |||
( 0x42, 'KEY_F9' ), | |||
( 0x43, 'KEY_F10' ), | |||
( 0x44, 'KEY_F11' ), | |||
( 0x45, 'KEY_F12' ), | |||
( 0x46, 'KEY_PRINTSCREEN' ), | |||
( 0x47, 'KEY_SCROLL_LOCK' ), | |||
( 0x48, 'KEY_PAUSE' ), | |||
( 0x49, 'KEY_INSERT' ), | |||
( 0x4A, 'KEY_HOME' ), | |||
( 0x4B, 'KEY_PAGE_UP' ), | |||
( 0x4C, 'KEY_DELETE' ), | |||
( 0x4D, 'KEY_END' ), | |||
( 0x4E, 'KEY_PAGE_DOWN' ), | |||
( 0x4F, 'KEY_RIGHT' ), | |||
( 0x50, 'KEY_LEFT' ), | |||
( 0x51, 'KEY_DOWN' ), | |||
( 0x52, 'KEY_UP' ), | |||
( 0x53, 'KEY_NUM_LOCK' ), | |||
( 0x54, 'KEYPAD_SLASH' ), | |||
( 0x55, 'KEYPAD_ASTERIX' ), | |||
( 0x56, 'KEYPAD_MINUS' ), | |||
( 0x57, 'KEYPAD_PLUS' ), | |||
( 0x58, 'KEYPAD_ENTER' ), | |||
( 0x59, 'KEYPAD_1' ), | |||
( 0x5A, 'KEYPAD_2' ), | |||
( 0x5B, 'KEYPAD_3' ), | |||
( 0x5C, 'KEYPAD_4' ), | |||
( 0x5D, 'KEYPAD_5' ), | |||
( 0x5E, 'KEYPAD_6' ), | |||
( 0x5F, 'KEYPAD_7' ), | |||
( 0x60, 'KEYPAD_8' ), | |||
( 0x61, 'KEYPAD_9' ), | |||
( 0x62, 'KEYPAD_0' ), | |||
( 0x63, 'KEYPAD_PERIOD' ), | |||
( 0x64, 'KEY_ISO_SLASH' ), | |||
( 0x65, 'KEY_APP' ), | |||
( 0x66, 'KEYBOARD_STATUS' ), # Used for indicating status or errors, not a key | |||
( 0x67, 'KEYPAD_EQUAL' ), | |||
( 0x68, 'KEY_F13' ), | |||
( 0x69, 'KEY_F14' ), | |||
( 0x6A, 'KEY_F15' ), | |||
( 0x6B, 'KEY_F16' ), | |||
( 0x6C, 'KEY_F17' ), | |||
( 0x6D, 'KEY_F18' ), | |||
( 0x6E, 'KEY_F19' ), | |||
( 0x6F, 'KEY_F20' ), | |||
( 0x70, 'KEY_F21' ), | |||
( 0x71, 'KEY_F22' ), | |||
( 0x72, 'KEY_F23' ), | |||
( 0x73, 'KEY_F24' ), | |||
( 0x74, 'KEY_EXEC' ), | |||
( 0x75, 'KEY_HELP' ), | |||
( 0x76, 'KEY_MENU' ), | |||
( 0x77, 'KEY_SELECT' ), | |||
( 0x78, 'KEY_STOP' ), | |||
( 0x79, 'KEY_AGAIN' ), | |||
( 0x7A, 'KEY_UNDO' ), | |||
( 0x7B, 'KEY_CUT' ), | |||
( 0x7C, 'KEY_COPY' ), | |||
( 0x7D, 'KEY_PASTE' ), | |||
( 0x7E, 'KEY_FIND' ), | |||
( 0x7F, 'KEY_MUTE' ), | |||
( 0x80, 'KEY_VOL_UP' ), | |||
( 0x81, 'KEY_VOL_DOWN' ), | |||
( 0x82, 'KEY_CAPS_TLOCK' ), # Toggle "Locking" Scroll Lock (Old keyboards with Locking Caps Lock) | |||
( 0x83, 'KEY_NUM_TLOCK' ), | |||
( 0x84, 'KEY_SCROLL_TLOCK' ), | |||
( 0x85, 'KEYPAD_COMMA' ), # Brazillian (See spec) | |||
( 0x86, 'KEYPAD_EQUAL_AS' ), # AS/400 Keyboard (See spec) | |||
( 0x87, 'KEY_INTER1' ), # KANJI1 - Brazillian and Japanese "Ru" and "-" | |||
( 0x88, 'KEY_INTER2' ), # KANJI2 - Japanese Katakana/Hiragana | |||
( 0x89, 'KEY_INTER3' ), # KANJI3 - Japanese Yen | |||
( 0x8A, 'KEY_INTER4' ), # KANJI4 - Japanese Henkan | |||
( 0x8B, 'KEY_INTER5' ), # KANJI5 - Japanese Muhenkan | |||
( 0x8C, 'KEY_INTER6' ), # KANJI6 - PC0x62 Comma (Ka-m-ma) | |||
( 0x8D, 'KEY_INTER7' ), # KANJI7 - Double-Byte/Single-Byte Toggle | |||
( 0x8E, 'KEY_INTER8' ), # KANJI8 - Undefined | |||
( 0x8F, 'KEY_INTER9' ), # KANJI9 - Undefined | |||
( 0x90, 'KEY_LANG1' ), # Korean Hangul/English Toggle | |||
( 0x91, 'KEY_LANG2' ), # Korean Hanja Conversion - Japanese Eisu | |||
( 0x92, 'KEY_LANG3' ), # Japanese Katakana Key (USB) | |||
( 0x93, 'KEY_LANG4' ), # Japanese Hiragana Key (USB) | |||
( 0x94, 'KEY_LANG5' ), # Japanese Zenkaku/Hankaku Key (USB) | |||
( 0x95, 'KEY_LANG6' ), # Reserved (Application Specific) | |||
( 0x96, 'KEY_LANG7' ), # Reserved (Application Specific) | |||
( 0x97, 'KEY_LANG8' ), # Reserved (Application Specific) | |||
( 0x98, 'KEY_LANG9' ), # Reserved (Application Specific) | |||
( 0x99, 'KEY_ALT_ERASE' ), # Special Erase (See Spec) | |||
( 0x9A, 'KEY_SYSREQ_ATT' ), # Modifier Type | |||
( 0x9B, 'KEY_CANCEL' ), | |||
( 0x9C, 'KEY_CLEAR' ), | |||
( 0x9D, 'KEY_PRIOR' ), | |||
( 0x9E, 'KEY_RETURN' ), | |||
( 0x9F, 'KEY_SEPARATOR' ), | |||
( 0xA0, 'KEY_OUT' ), | |||
( 0xA1, 'KEY_OPER' ), | |||
( 0xA2, 'KEY_CLEAR_AGAIN' ), | |||
( 0xA3, 'KEY_CRSEL_PROPS' ), | |||
( 0xA4, 'KEY_EXSEL' ), | |||
# 0xA5 - 0xAF Reserved | |||
( 0xB0, 'KEYPAD_00' ), | |||
( 0xB1, 'KEYPAD_000' ), | |||
( 0xB2, 'KEY_1000_SEP' ), | |||
( 0xB3, 'KEY_DECIMAL_SEP' ), | |||
( 0xB4, 'KEY_CURRENCY_MAIN' ), | |||
( 0xB5, 'KEY_CURRENCY_SUB' ), | |||
( 0xB6, 'KEYPAD_LPAREN' ), | |||
( 0xB7, 'KEYPAD_RPAREN' ), | |||
( 0xB8, 'KEYPAD_LBRACE' ), | |||
( 0xB9, 'KEYPAD_RBRACE' ), | |||
( 0xBA, 'KEYPAD_TAB' ), | |||
( 0xBB, 'KEYPAD_BACKSPACE' ), | |||
( 0xBC, 'KEYPAD_A' ), | |||
( 0xBD, 'KEYPAD_B' ), | |||
( 0xBE, 'KEYPAD_C' ), | |||
( 0xBF, 'KEYPAD_D' ), | |||
( 0xC0, 'KEYPAD_E' ), | |||
( 0xC1, 'KEYPAD_F' ), | |||
( 0xC2, 'KEYPAD_XOR' ), | |||
( 0xC3, 'KEYPAD_CHEVRON' ), | |||
( 0xC4, 'KEYPAD_PERCENT' ), | |||
( 0xC5, 'KEYPAD_LTHAN' ), | |||
( 0xC6, 'KEYPAD_GTHAN' ), | |||
( 0xC7, 'KEYPAD_BITAND' ), | |||
( 0xC8, 'KEYPAD_AND' ), | |||
( 0xC9, 'KEYPAD_BITOR' ), | |||
( 0xCA, 'KEYPAD_OR' ), | |||
( 0xCB, 'KEYPAD_COLON' ), | |||
( 0xCC, 'KEYPAD_POUND' ), | |||
( 0xCD, 'KEYPAD_SPACE' ), | |||
( 0xCE, 'KEYPAD_AT' ), | |||
( 0xCF, 'KEYPAD_EXCLAIM' ), | |||
( 0xD0, 'KEYPAD_MEM_STORE' ), | |||
( 0xD1, 'KEYPAD_MEM_RECALL' ), | |||
( 0xD2, 'KEYPAD_MEM_CLEAR' ), | |||
( 0xD3, 'KEYPAD_MEM_ADD' ), | |||
( 0xD4, 'KEYPAD_MEM_SUB' ), | |||
( 0xD5, 'KEYPAD_MEM_MULT' ), | |||
( 0xD6, 'KEYPAD_MEM_DIV' ), | |||
( 0xD7, 'KEYPAD_PLUS_MINUS' ), | |||
( 0xD8, 'KEYPAD_CLEAR' ), | |||
( 0xD9, 'KEYPAD_CLEAR_ENTRY' ), | |||
( 0xDA, 'KEYPAD_BINARY' ), | |||
( 0xDB, 'KEYPAD_OCTAL' ), | |||
( 0xDC, 'KEYPAD_DECIMAL' ), | |||
( 0xDD, 'KEYPAD_HEX' ), | |||
# 0xDE - 0xDF Reserved | |||
( 0xE0, 'KEY_LCTRL' ), | |||
( 0xE1, 'KEY_LSHIFT' ), | |||
( 0xE2, 'KEY_LALT' ), | |||
( 0xE3, 'KEY_LGUI' ), | |||
( 0xE4, 'KEY_RCTRL' ), | |||
( 0xE5, 'KEY_RSHIFT' ), | |||
( 0xE6, 'KEY_RALT' ), | |||
( 0xE7, 'KEY_RGUI' ), | |||
# 0xE8 - 0xFFFF Reserved, using 0xF0 to 0xFF for function key placeholders | |||
( 0xF0, 'KEY_FUN1' ), | |||
( 0xF1, 'KEY_FUN2' ), | |||
( 0xF2, 'KEY_FUN3' ), | |||
( 0xF3, 'KEY_FUN4' ), | |||
( 0xF4, 'KEY_FUN5' ), | |||
( 0xF5, 'KEY_FUN6' ), | |||
( 0xF6, 'KEY_FUN7' ), | |||
( 0xF7, 'KEY_FUN8' ), | |||
( 0xF8, 'KEY_FUN9' ), | |||
( 0xF9, 'KEY_FUN10' ), | |||
( 0xFA, 'KEY_FUN11' ), | |||
( 0xFB, 'KEY_FUN12' ), | |||
( 0xFC, 'KEY_FUN13' ), | |||
( 0xFD, 'KEY_FUN14' ), | |||
( 0xFE, 'KEY_FUN15' ), | |||
( 0xFF, 'KEY_FUN16' ), | |||
]) | |||
# Lookup for KLL defined HID values, internally the compiler uses numbers to combine the keymaps | |||
kll_hid_lookup_dictionary = dict([ | |||
( 'A', 0x04 ), | |||
( 'B', 0x05 ), | |||
( 'C', 0x06 ), | |||
( 'D', 0x07 ), | |||
( 'E', 0x08 ), | |||
( 'F', 0x09 ), | |||
( 'G', 0x0A ), | |||
( 'H', 0x0B ), | |||
( 'I', 0x0C ), | |||
( 'J', 0x0D ), | |||
( 'K', 0x0E ), | |||
( 'L', 0x0F ), | |||
( 'M', 0x10 ), | |||
( 'N', 0x11 ), | |||
( 'O', 0x12 ), | |||
( 'P', 0x13 ), | |||
( 'Q', 0x14 ), | |||
( 'R', 0x15 ), | |||
( 'S', 0x16 ), | |||
( 'T', 0x17 ), | |||
( 'U', 0x18 ), | |||
( 'V', 0x19 ), | |||
( 'W', 0x1A ), | |||
( 'X', 0x1B ), | |||
( 'Y', 0x1C ), | |||
( 'Z', 0x1D ), | |||
( '1', 0x1E ), | |||
( '2', 0x1F ), | |||
( '3', 0x20 ), | |||
( '4', 0x21 ), | |||
( '5', 0x22 ), | |||
( '6', 0x23 ), | |||
( '7', 0x24 ), | |||
( '8', 0x25 ), | |||
( '9', 0x26 ), | |||
( '0', 0x27 ), | |||
( 'ENTER', 0x28 ), | |||
( 'ESC', 0x29 ), | |||
( 'BACKSPACE', 0x2A ), | |||
( 'TAB', 0x2B ), | |||
( 'SPACE', 0x2C ), | |||
( '-', 0x2D ), ( 'MINUS', 0x2D ), | |||
( '=', 0x2E ), ( 'EQUALS', 0x2E ), ( 'EQUAL', 0x2E ), | |||
( '{', 0x2F ), ( 'LEFT BRACE', 0x2F ), ( 'LBRACE', 0x2F ), | |||
( '}', 0x30 ), ( 'RIGHT BRACE', 0x30 ), ( 'RBRACE', 0x30 ), | |||
( '\\', 0x31 ), ( 'BACKSLASH', 0x31 ), | |||
( '#', 0x32 ), ( 'NUMBER', 0x32 ), ( 'HASH', 0x32 ), | |||
( ';', 0x33 ), ( 'SEMICOLON', 0x33 ), | |||
( "'", 0x34 ), ( 'QUOTE', 0x34 ), | |||
( '`', 0x35 ), ( 'BACKTICK', 0x35 ), | |||
( ',', 0x36 ), ( 'COMMA', 0x36 ), | |||
( '.', 0x37 ), ( 'PERIOD', 0x37 ), | |||
( '/', 0x38 ), ( 'SLASH', 0x38 ), | |||
( 'CAPSLOCK', 0x39 ), | |||
( 'F1', 0x3A ), | |||
( 'F2', 0x3B ), | |||
( 'F3', 0x3C ), | |||
( 'F4', 0x3D ), | |||
( 'F5', 0x3E ), | |||
( 'F6', 0x3F ), | |||
( 'F7', 0x40 ), | |||
( 'F8', 0x41 ), | |||
( 'F9', 0x42 ), | |||
( 'F10', 0x43 ), | |||
( 'F11', 0x44 ), | |||
( 'F12', 0x45 ), | |||
( 'PRINTSCREEN', 0x46 ), | |||
( 'SCROLLLOCK', 0x47 ), | |||
( 'PAUSE', 0x48 ), | |||
( 'INSERT', 0x49 ), | |||
( 'HOME', 0x4A ), | |||
( 'PAGEUP', 0x4B ), | |||
( 'DELETE', 0x4C ), | |||
( 'END', 0x4D ), | |||
( 'PAGEDOWN', 0x4E ), | |||
( 'RIGHT', 0x4F ), | |||
( 'LEFT', 0x50 ), | |||
( 'DOWN', 0x51 ), | |||
( 'UP', 0x52 ), | |||
( 'NUMLOCK', 0x53 ), | |||
( 'P/', 0x54 ), ( 'KEYPAD SLASH', 0x54 ), | |||
( 'P*', 0x55 ), ( 'KEYPAD ASTERIX', 0x55 ), | |||
( 'P-', 0x56 ), ( 'KEYPAD MINUS', 0x56 ), | |||
( 'P+', 0x57 ), ( 'KEYPAD PLUS', 0x57 ), | |||
( 'PENTER', 0x58 ), ( 'KEYPAD ENTER', 0x58 ), | |||
( 'P1', 0x59 ), ( 'KEYPAD 1', 0x59 ), | |||
( 'P2', 0x5A ), ( 'KEYPAD 2', 0x5A ), | |||
( 'P3', 0x5B ), ( 'KEYPAD 3', 0x5B ), | |||
( 'P4', 0x5C ), ( 'KEYPAD 4', 0x5C ), | |||
( 'P5', 0x5D ), ( 'KEYPAD 5', 0x5D ), | |||
( 'P6', 0x5E ), ( 'KEYPAD 6', 0x5E ), | |||
( 'P7', 0x5F ), ( 'KEYPAD 7', 0x5F ), | |||
( 'P8', 0x60 ), ( 'KEYPAD 8', 0x60 ), | |||
( 'P9', 0x61 ), ( 'KEYPAD 9', 0x61 ), | |||
( 'P0', 0x62 ), ( 'KEYPAD 0', 0x62 ), | |||
( 'P.', 0x63 ), ( 'KEYPAD PERIOD', 0x63 ), | |||
( 'ISO/', 0x64 ), ( 'ISO SLASH', 0x64 ), | |||
( 'APP', 0x65 ), | |||
( 'P=', 0x67 ), ( 'KEYPAD EQUAL', 0x67 ), | |||
( 'F13', 0x68 ), | |||
( 'F14', 0x69 ), | |||
( 'F15', 0x6A ), | |||
( 'F16', 0x6B ), | |||
( 'F17', 0x6C ), | |||
( 'F18', 0x6D ), | |||
( 'F19', 0x6E ), | |||
( 'F20', 0x6F ), | |||
( 'F21', 0x70 ), | |||
( 'F22', 0x71 ), | |||
( 'F23', 0x72 ), | |||
( 'F24', 0x73 ), | |||
( 'EXEC', 0x74 ), | |||
( 'HELP', 0x75 ), | |||
( 'MENU', 0x76 ), | |||
( 'SELECT', 0x77 ), | |||
( 'STOP', 0x78 ), | |||
( 'AGAIN', 0x79 ), | |||
( 'UNDO', 0x7A ), | |||
( 'CUT', 0x7B ), | |||
( 'COPY', 0x7C ), | |||
( 'PASTE', 0x7D ), | |||
( 'FIND', 0x7E ), | |||
( 'MUTE', 0x7F ), | |||
( 'VOLUMEUP', 0x80 ), | |||
( 'VOLUMEDOWN', 0x81 ), | |||
( 'CAPSTOGGLELOCK', 0x82 ), | |||
( 'NUMTOGGLELOCK', 0x83 ), | |||
( 'SCROLLTOGGLELOCK', 0x84 ), | |||
( 'P,', 0x85 ), | |||
( 'KEYPAD AS400 EQUAL', 0x86 ), | |||
( 'INTER1', 0x87 ), ( 'KANJI1', 0x87 ), | |||
( 'INTER2', 0x88 ), ( 'KANJI2', 0x88 ), ( 'KANA', 0x88 ), | |||
( 'INTER3', 0x89 ), ( 'KANJI3', 0x89 ), ( 'YEN', 0x89 ), | |||
( 'INTER4', 0x8A ), ( 'KANJI4', 0x8A ), ( 'HENKAN', 0x8A ), | |||
( 'INTER5', 0x8B ), ( 'KANJI5', 0x8B ), ( 'MUHENKAN', 0x8B ), | |||
( 'INTER6', 0x8C ), ( 'KANJI6', 0x8C ), | |||
( 'INTER7', 0x8D ), ( 'KANJI7', 0x8D ), ( 'BYTETOGGLE', 0x8D ), | |||
( 'INTER8', 0x8E ), ( 'KANJI8', 0x8E ), | |||
( 'INTER9', 0x8F ), ( 'KANJI9', 0x8F ), | |||
( 'LANG1', 0x90 ), ( 'HANGULENGLISH', 0x90 ), | |||
( 'LANG2', 0x91 ), ( 'HANJA', 0x91 ), ( 'EISU', 0x91 ), | |||
( 'LANG3', 0x92 ), ( 'KATAKANA', 0x92 ), | |||
( 'LANG4', 0x93 ), ( 'HIRAGANA', 0x93 ), | |||
( 'LANG5', 0x94 ), ( 'ZENKAKUHANKAKU', 0x94 ), | |||
( 'LANG6', 0x95 ), | |||
( 'LANG7', 0x96 ), | |||
( 'LANG8', 0x97 ), | |||
( 'LANG9', 0x98 ), | |||
( 'ALTERASE', 0x99 ), | |||
( 'SYSREQATT', 0x9A ), | |||
( 'CANCEL', 0x9B ), | |||
( 'CLEAR', 0x9C ), | |||
( 'PRIOR', 0x9D ), | |||
( 'RETURN', 0x9E ), | |||
( 'SEP', 0x9F ), ( 'SEPARATOR', 0x9F ), | |||
( 'OUT', 0xA0 ), | |||
( 'OPER', 0xA1 ), | |||
( 'CLEAR_AGAIN', 0xA2 ), | |||
( 'CRSEL_PROPS', 0xA3 ), | |||
( 'EXSEL', 0xA4 ), | |||
( 'P00', 0xB0 ), ( 'KEYPAD 00', 0xB0 ), | |||
( 'P000', 0xB1 ), ( 'KEYPAD 000', 0xB1 ), | |||
( '1000SEP', 0xB2 ), ( 'THOUSANDSEPARATOR', 0xB2 ), | |||
( 'DECIMALSEP', 0xB3 ), ( 'DECIMALSEPARATOR', 0xB3 ), | |||
( 'CURRENCY', 0xB4 ), ( 'CURRENCYUNIT', 0xB4 ), | |||
( 'CURRENCYSUB', 0xB5 ), ( 'CURRENCYSUBUNIT', 0xB5 ), | |||
( 'P(', 0xB6 ), ( 'KEYPAD LEFT PARENTHESES', 0xB6 ), | |||
( 'P)', 0xB7 ), ( 'KEYPAD RIGHT PARENTHESES', 0xB7 ), | |||
( 'P{', 0xB8 ), ( 'KEYPAD LEFT BRACE', 0xB8 ), | |||
( 'P}', 0xB9 ), ( 'KEYPAD RIGHT BRACE', 0xB9 ), | |||
( 'PTAB', 0xBA ), ( 'KEYPAD TAB', 0xBA ), | |||
( 'PBACKSPACE', 0xBB ), ( 'KEYPAD BACKSPACE', 0xBB ), | |||
( 'PA', 0xBC ), ( 'KEYPAD A', 0xBC ), | |||
( 'PB', 0xBD ), ( 'KEYPAD B', 0xBD ), | |||
( 'PC', 0xBE ), ( 'KEYPAD C', 0xBE ), | |||
( 'PD', 0xBF ), ( 'KEYPAD D', 0xBF ), | |||
( 'PE', 0xC0 ), ( 'KEYPAD E', 0xC0 ), | |||
( 'PF', 0xC1 ), ( 'KEYPAD F', 0xC1 ), | |||
( 'PXOR', 0xC2 ), ( 'KEYPAD XOR', 0xC2 ), | |||
( 'P^', 0xC3 ), ( 'KEYPAD CHEVRON', 0xC3 ), | |||
( 'P%', 0xC4 ), ( 'KEYPAD PERCENT', 0xC4 ), | |||
( 'P<', 0xC5 ), ( 'KEYPAD LESSTHAN', 0xC5 ), | |||
( 'P>', 0xC6 ), ( 'KEYPAD GREATERTHAN', 0xC6 ), | |||
( 'P&', 0xC7 ), ( 'KEYPAD BITAND', 0xC7 ), | |||
( 'P&&', 0xC8 ), ( 'KEYPAD AND', 0xC8 ), | |||
( 'P|', 0xC9 ), ( 'KEYPAD BITOR', 0xC9 ), | |||
( 'P||', 0xCA ), ( 'KEYPAD OR', 0xCA ), | |||
( 'P:', 0xCB ), ( 'KEYPAD COLON', 0xCB ), | |||
( 'P#', 0xCC ), ( 'KEYPAD NUMBER', 0xCC ), ( 'KEYPAD HASH', 0xCC ), | |||
( 'PSPACE', 0xCD ), ( 'KEYPAD SPACE', 0xCD ), | |||
( 'P@', 0xCE ), ( 'KEYPAD AT', 0xCE ), | |||
( 'P!', 0xCF ), ( 'KEYPAD EXCLAIM', 0xCF ), | |||
( 'PMEMSTORE', 0xD0 ), ( 'KEYPAD MEMSTORE', 0xD0 ), | |||
( 'PMEMRECALL', 0xD1 ), ( 'KEYPAD MEMRECALL', 0xD1 ), | |||
( 'PMEMCLEAR', 0xD2 ), ( 'KEYPAD MEMCLEAR', 0xD2 ), | |||
( 'PMEMADD', 0xD3 ), ( 'KEYPAD MEMADD', 0xD3 ), | |||
( 'PMEMSUB', 0xD4 ), ( 'KEYPAD MEMSUB', 0xD4 ), | |||
( 'PMEMMULT', 0xD5 ), ( 'KEYPAD MEMMULT', 0xD5 ), | |||
( 'PMEMDIV', 0xD6 ), ( 'KEYPAD MEMDIV', 0xD6 ), | |||
( 'P+/-', 0xD7 ), ( 'KEYPAD PLUSMINUS', 0xD7 ), | |||
( 'PCLEAR', 0xD8 ), ( 'KEYPAD CLEAR', 0xD8 ), | |||
( 'PCLEARENTRY', 0xD9 ), ( 'KEYPAD CLEARENTRY', 0xD9 ), | |||
( 'PBINARY', 0xDA ), ( 'KEYPAD BINARY', 0xDA ), | |||
( 'POCTAL', 0xDB ), ( 'KEYPAD OCTAL', 0xDB ), | |||
( 'PDECIMAL', 0xDC ), ( 'KEYPAD DECIMAL', 0xDC ), | |||
( 'PHEX', 0xDD ), ( 'KEYPAD HEX', 0xDD ), | |||
( 'LCTRL', 0xE0 ), ( 'LEFT CTRL', 0xE0 ), ( 'CTRL', 0xE0 ), | |||
( 'LSHIFT', 0xE1 ), ( 'LEFT SHIFT', 0xE1 ), ( 'SHIFT', 0xE1 ), | |||
( 'LALT', 0xE2 ), ( 'LEFT ALT', 0xE2 ), ( 'ALT', 0xE2 ), | |||
( 'LGUI', 0xE3 ), ( 'LEFT GUI', 0xE3 ), ( 'GUI', 0xE3 ), | |||
( 'RCTRL', 0xE4 ), ( 'RIGHT CTRL', 0xE4 ), | |||
( 'RSHIFT', 0xE5 ), ( 'RIGHT SHIFT', 0xE5 ), | |||
( 'RALT', 0xE6 ), ( 'RIGHT ALT', 0xE6 ), | |||
( 'RGUI', 0xE7 ), ( 'RIGHT GUI', 0xE7 ), | |||
( 'FUN1', 0xF0 ), ( 'FUNCTION1', 0xF0 ), ( 'FUN', 0xF0 ), | |||
( 'FUN2', 0xF1 ), ( 'FUNCTION2', 0xF1 ), | |||
( 'FUN3', 0xF2 ), ( 'FUNCTION3', 0xF2 ), | |||
( 'FUN4', 0xF3 ), ( 'FUNCTION4', 0xF3 ), | |||
( 'FUN5', 0xF4 ), ( 'FUNCTION5', 0xF4 ), | |||
( 'FUN6', 0xF5 ), ( 'FUNCTION6', 0xF5 ), | |||
( 'FUN7', 0xF6 ), ( 'FUNCTION7', 0xF6 ), | |||
( 'FUN8', 0xF7 ), ( 'FUNCTION8', 0xF7 ), | |||
( 'FUN9', 0xF8 ), ( 'FUNCTION9', 0xF8 ), | |||
( 'FUN10', 0xF9 ), ( 'FUNCTION10', 0xF9 ), | |||
( 'FUN11', 0xFA ), ( 'FUNCTION11', 0xFA ), | |||
( 'FUN12', 0xFB ), ( 'FUNCTION12', 0xFB ), | |||
( 'FUN13', 0xFC ), ( 'FUNCTION13', 0xFC ), | |||
( 'FUN14', 0xFD ), ( 'FUNCTION14', 0xFD ), | |||
( 'FUN15', 0xFE ), ( 'FUNCTION15', 0xFE ), | |||
( 'FUN16', 0xFF ), ( 'FUNCTION16', 0xFF ), | |||
]) | |||
@@ -0,0 +1,114 @@ | |||
/* Copyright (C) 2014 by Jacob Alexander | |||
* | |||
* This file is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This file is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this file. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
// Generated MSG /w timestamp and compiler information | |||
#ifndef __generatedKeymap_h | |||
#define __generatedKeymap_h | |||
// ----- Includes ----- | |||
// KLL Include | |||
#include <kll.h> | |||
// ----- Capabilities ----- | |||
// Indexed Capabilities Table | |||
<|CapabilitiesList|> | |||
// -- Result Macros | |||
// Result Macro Guides | |||
<|ResultMacros|> | |||
// -- Result Macro List | |||
// Indexed Table of Result Macros | |||
<|ResultMacroList|> | |||
// -- Trigger Macros | |||
// Trigger Macro Guides | |||
<|TriggerMacros|> | |||
// -- Trigger Macro List | |||
// Indexed Table of Trigger Macros | |||
<|TriggerMacroList|> | |||
// ----- Trigger Maps ----- | |||
// MaxScanCode | |||
// - This is retrieved from the KLL configuration | |||
// - Should be corollated with the max scan code in the scan module | |||
// - Maximum value is 0x100 (0x0 to 0xFF) | |||
// - Increasing it beyond the keyboard's capabilities is just a waste of ram... | |||
#define MaxScanCode <MaxScanCode> | |||
// -- Trigger Lists | |||
// | |||
// Index 0: # of triggers in list | |||
// Index n: pointer to trigger macro - use tm() macro | |||
// - Default Layer - | |||
<|DefaultLayerTriggerList|> | |||
// - Partial Layers - | |||
<|PartialLayerTriggerLists|> | |||
// -- ScanCode Indexed Maps | |||
// Maps to a trigger list of macro pointers | |||
// _ | |||
// <scan code> -> |T| | |||
// |r| -> <trigger macro pointer 1> | |||
// |i| | |||
// |g| -> <trigger macro pointer 2> | |||
// |g| | |||
// |e| -> <trigger macro pointer 3> | |||
// |r| | |||
// |s| -> <trigger macro pointer n> | |||
// - | |||
// - Default Map for ScanCode Lookup - | |||
<|DefaultLayerScanMap|> | |||
// - Partial Layer ScanCode Lookup Maps - | |||
<|PartialLayerScanMaps|> | |||
// ----- Layer Index ----- | |||
// -- Layer Index List | |||
// | |||
// Index 0: Default map | |||
// Index n: Additional layers | |||
<|LayerIndexList|> | |||
#endif // __generatedKeymap_h | |||