An experimental "compiler". Write and compile a small language to Linux's USB HID keyboard scancodes.
Sometimes, you need a quick solution to write USB HID scancodes, and the process will be painful.
Why write scancodes by hand when you can create a small language and a compiler to do this job for you instead? ;)
I'm not a compiler designer, or a language designer, I'm just a guy who likes to tinker with stuff -- I know this is far from being called a real compiler.
hid-compiler
will accept any ASCII symbol.
An ASCII string is defined by one or more ASCII symbol:
- "a" is an accepted string
- " " is an accepted string
- "" is not an accepted string (empty, thus does not contain any ASCII symbol)
- "hello" is an accepted string
hid-compiler
supports the bare minimum key modifiers available on a standard keyboard:
LCTRL
(left control)LSHIFT
(left shift)LALT
(left alt)LMETA
(left Super/Windows/Command key)RCTRL
(right control)RSHIFT
(right shift)RALT
(right alt)RMETA
(right Super/Windows/Command key)
hid-compiler
supports string modifiers and character modifiers.
A string modifier applies a key modifier operation on a string, and it's defined as follows:
[key-modifier]>STRING
Where "STRING" is a well-formed ASCII string as defined previously.
A character modifier applies a key modifier operation on the character on the rightmost side of the declaration; it's defined as follows:
[key-modifier]:STRING
The modifier operation will be applied only on the first character of the "STRING" ASCII string (the "S").
ST[key-modifier]:RING
The modifier operation will be applied on the "R" character of the "STRING" ASCII string.
The input string is parsed as a linked list composed of tokens
.
A token
holds the original, un-parsed string, the parsed string without any modifier, either a stringModifier
or a characterModifier
.
Both stringModifier
and characterModifier
holds a reference to the string or character they have to modify, and a modifier
.
A modifier
is a simple data type, mostly created for stylish purposes - I could have used a simple int
, but that would have been ugly.
While a stringModifier
is conceptually easier to understand - it only holds a string and a modifier
- characterModifier
is more complex because of the way it binds to a character.
This kind of modifier can appear everywhere in the string, thus I needed to memorize the sub-string before the modifier (Left
) and after (Right
), including the character to modify.
The real action starts in the Compile(s string)
, in compiler/Compile.go
.
This function starts by creating a linked list; each word divided a space will be tokenized by tokenize(s string)
and put into the list.
tokenize(s string)
calls both matchStringModifier(s string)
and matchCharModifier(s string)
to check if the string contains a character modifier, or a string modifier.
Each of these function will return a token
containig one of the two modifier fields non-nil.
If both functions don't return, it means the string doesn't contain any modifier, and thus can be parsed withouth any special operation - the resulting token
contains the modifier fields set to nil
.
After the tokenizing phase, it's now time to actually translate the tokens to HID scancodes.
Since there are 3 types of strings currently built in, 3 functions has been written to accomplish the translation:
generateHIDPayloadForString(s stringModifier)
generateHIDPayloadForCharacter(c characterModifier)
generateHIDPayloadForCharacter(s string)
generateHIDPayloadForSpace()
To make the translation easier, the hidPayload
type has been defined.
hidPayload
holds a reference to the Modifier to apply to a character (which is a string
anyway), and the character itself.
A String()
method for hidPayload
has been defined too, which returns the hex-formatted scancode.
In a nutshell, these function will create the hex-formatted string by concatenating the String()
output of all the hidPayload
created with the content of the characterModifier
or stringModifier
(if any) passed as argument.
generateHIDPayloadForCharacter(c characterModifier)
is a little bit different, because it has to account for the left and right part of the original string.
generateHIDPayloadForSpace()
is much simpler: it just creates a "space" character, encoded.
These functions will be called while cycling through the list, and their output will be concatenated.