Archived
1
0

Initial source dump.

- 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.
This commit is contained in:
Jacob Alexander 2014-09-02 10:03:50 -07:00
parent 3057d43e49
commit 1f0bf65417
13 changed files with 1973 additions and 0 deletions

63
.gitignore vendored Normal file
View File

@ -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]

2
README
View File

@ -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
backends/__init__.py Normal file
View File

82
backends/kiibohd.py Normal file
View File

@ -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) )

View File

133
funcparserlib/lexer.py Normal file
View File

@ -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)

409
funcparserlib/parser.py Normal file
View File

@ -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()

49
funcparserlib/util.py Normal file
View File

@ -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)

505
kll.py Executable file
View File

@ -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
kll_lib/__init__.py Normal file
View File

114
kll_lib/containers.py Normal file
View File

@ -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

502
kll_lib/hid_dict.py Normal file
View File

@ -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 ),
])

114
templateKeymap.h Normal file
View File

@ -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