INPUT-METDHO ::= '(' TITLE MAP-LIST MODULE-LIST ? STATE-LIST ')' TITLE ::= '(' title MTEXT ')' MAP-LIST ::= '(' 'map' MAP * ')' MAP ::= '(' MAP-NAME RULE * ')' MAP-NAME ::= symbol RULE ::= '(' KEYSEQ MAP-ACTION * ')' KEYSEQ ::= mtext | '(' [ symbol | integer ] * ')' MAP-ACTION ::= ACTION ACTION ::= mtext | CANDIDATES | '(' ACTION-NAME ACTION-ARG * ')' CANDIDATES ::= '(' CANDIDATE-GROUP * ')' (in ASCTION, MTEXT is a short form of "(insert MTEXT)" CANDIDATES is a short form of "(candidates CANDIDATE-GROUP *)") ACTION-NAME ::= 'insert' | 'candidates' | 'delete' | 'select' | 'select-group' | 'move' | 'mark' | 'pushback' | 'undo' | 'shift' | 'call' ACTION-ARG ::= integer | symbol | mtext | CANDIDATE-GROUP CANDIDATE-GROUP ::= mtext | '(' mtext * ')' PREDEFINED-SYMBOL ::= '@0' | '@1' | '@2' | '@3' | '@4' | '@5' | '@6' | '@7' | '@8' | '@9' | '@<' | '@=' | '@>' | '@-' | '@+' MARKER ::= PREDEFINED-SYMBOL | symbol CANDIDATE-INDEX ::= PREDEFINED-SYMBOL CANDIDATE-GROUP-INDEX ::= PREDEFINED-SYMBOL ;; The first five actions ('insert' .. 'select-group') are for ;; editing a text in the preediting buffer. The buffer can keep ;; multiple markers. Each marker is represented by a symbol, and ;; keeps a position between characters in the preediting buffer. ;; PREDEFINED-SYMBOL has special meanings when used as MARKER: ;; '@0', '@1', ... '@9' ;; The 0th, 1th, ... 9th position. ;; '@<', '@=', '@>' ;; The first, current, and end position. ;; '@-', '@+' ;; The previous and next position. ;; PREDEFINED-SYMBOL has special meanings when used as CANDIDATE-INDEX: ;; '@0', '@1', ... '@9' ;; The 0th, 1th, ... 9th candidate of the current candidate group. ;; '@<', '@=', '@>' ;; The first, current, and end candidate of the current candidate ;; group. ;; '@-' ;; The previous candidate. If the current candidate is the ;; first of the current candidate group, the last candidate of ;; the previous candidate group. ;; '@+' ;; The next candidate. If the current candidate is the last of ;; the current candidate group, the first candidate of the ;; previous candidate group. ;; PREDEFINED-SYMBOL has special meanings when used as CANDIDATE-INDEX: ;; '@0', '@1', ... '@9' ;; The 0th, 1th, ... 9th candidate of the current candidate group. ;; '@<', '@=', '@>' ;; The first, current, and end candidate of the current candidate ;; group. ;; '@-' ;; The previous candidate. If the current candidate is the ;; first of the current candidate group, the last candidate of ;; the previous candidate group. ;; '@+' ;; The next candidate. If the current candidate is the last of ;; the current candidate group, the first candidate of the ;; previous candidate group. ;; PREDEFINED-SYMBOL has special meanings when used as ;; CANDIDATE-GROUP-INDEX: ;; '@0', '@1', ... '@9' ;; The 0th, 1th, ... 9th candidate group. ;; '@<', '@=', '@>' ;; The first, current, and end candidate group. ;; '@-', '@+' ;; The previous and the next candidate group. ;; (insert MTEXT) inserts MTEXT before @=. ;; (candidates CANDIDATE-GROUP *) set the current candidates list ;; to the list of arguments, set the current candidate group to the ;; first argument, insert the first candidate of the current group ;; before @=, and mark the inserted text as the current candidate. ;; Each element of CANDIDATE-GROUP represents a candidate, i.e. if ;; CANDIDATE-GROUP is MTEXT, each character in MTEXT is a ;; candidate, if CANDIDATE-GROUP is a list of MTEXT, each MTEXT is ;; a candidate. ;; (select CANDIDATE-INDEX) replaces the current candidate with ;; what specified by CANDIDATE-INDEX. If a candidate of the ;; different candidate group is specified, set the current ;; candidate group to that group. ;; (select-group CANDIDATE-GROUP-INDEX) sets the current candidate ;; group to a group indicated by CANDIDATE-GROUP-INDEX, and relaces ;; the current candiate with the candiate of the same index in the ;; new group. ;; (delete MARKER) deletes characters between @= and the position ;; specified by MARKER. ;; (move MARKER) sets @= to the position of specified by MARKER. ;; (mark MARKER) sets MARKER to the position of @=. MARKER must ;; not be PREDEFINED-SYMBOL. ;; (pushback) pushbacks the latest key events to the event queue. ;; (undo) cancels the last key events. ;; (shift STATE-NAME) shifts the current state to the state ;; specified by STATE-NAME. ;; (call FUNCTION ARG *) calls the function FUNCTION of an external ;; module. FUNCTION must be defined in MODULE-LIST. ;; The function is called with a property list (MPlist *) that has ;; these properties in this order. ;; KEY VALUE ;; --- ----- ;; mtext The current preedit text. ;; symbol The current state name (MSymbol). ;; The remaining properties (if any) are ARGs. ;; ;; The function must return a property list (MPlist *) that ;; represents a list of ACTIONs to take. MODULE-LIST ::= '(' 'module' MODULE * ')' MODULE ::= '(' MODULE-NAME FUNCTION * ')' MODULE-NAME ::= mtext FUNCTION ::= symbol STATE-LIST ::= '(' 'state' STATE * ')' STATE ::= '(' STATE-NAME BRANCH * ')' STATE-NAME ::= symbol BRANCH ::= '(' [ MAP-NAME | 'nil' ] BRANCH-ACTION * ')' ;; If MAP-NAME is specified, it must be a name of a map defined in ;; MAP-LIST. Otherwise, BRANCH is the default branch of STATE. BRANCH-ACTION ::= ACTION ;; Example: (title "sample") (maps (single ("a" "A") ("b" "B")) (double ("bb" (("BB" "Bb")))) (select ((Left) (select @-)) ((Right) (select @+)))) (states (init (single) (double (shift selection))) (selection (select))) ;; When this input method is loaded, the following state transition ;; machine is created. ;; STATE-TRANSITION-MACHINE ::= ;; '(' STATE-NAME ROOT-MAP ')' * ;; ROOT-MAP ::= TRANSITION-MAP ;; TRANSITION-MAP ::= ;; '(' INDEX [ KEY | 'nil' ] ;; MAP-ACTIONS TRANSITION-MAPS BRANCH-ACTIONS ')' ;; TRANSITION-MAPS ::= ;; '(' TRANSITION-MAP * ')' ;; MAP-ACTIONS ::= ;; '(' MAP-ACTION * ')' ;; BRANCH-ACTIONS ::= ;; '(' BRANCH-ACTION * ')' (init (#0 nil nil ((#1 'a' ((insert "A")) nil nil) (#2 'b' ((insert "B")) ((#3 'b' ((candidates (("BB" "Bb")))) nil (shift selection))) nil)) nil)) (selection (#4 nil nil ((#5 'Left' ((delete @<) (select @-)) nil nil) (#6 'Right' ((delete @<) (select @+)) nil nil)) nil)) ;; The state transition machine keeps these things: ;; STATE: the current state, initially 'init'. ;; MAP: current transition map, initially #0. ;; PREEDIT: the preediting buffer, initially empty. ;; MARKERS: the positions assigned to each marker, @= is initially 0. ;; PRODUCED: the produced text, initially empty. ;; CANDIDATE-LIST: the current candidate group list, initially NULL. ;; CANDIDATE-GROUP: the current candidate group, initially NULL. ;; CANDIDATE: the the current candidate, initially NULL. ;; ;; When MAP is changed to the root map of the initial state, PREEDIT ;; is concatenated to PRODUCED and reset to empty. This way, the ;; machine produces a text. ;; ;; The machine accepts one key KEY, handles it while updating internal ;; information, and return 'nil' (if KEY is correctly handled) or KEY ;; (if KEY can't be handled in the machine). ;; Here we describes how the key sequence: ;; 'a' 'b' 'b' 'Right' 'b' 'a' ;; is handled by the machine and "aBbba" is produced. ;; 'a' arrives. ;; ;; Lookup the transition maps of #0 for 'a' and find #1. Change MAP ;; to #1. Perform its map actions. Now PREEDIT contains "a". As it ;; has no sub transition maps, no branch actions, no state to shift, ;; change MAP to #0, the root map of the current state. ;; ;; As we have changed MAP to the root map of the initial map, ;; concatenate PREEDIT to PRODUCED, and reset PREEDIT to empty. ;; ;; Now we have consumed the key. As MAP has sub transition maps, wait ;; for the next key. ;; 'b' arrives. ;; ;; Lookup #0 for 'b' and find #2. Change MAP to #2. Perform the map ;; actions. Now, PREEDIT contains "B". As it has sub transition ;; maps, and we have consumed the key, wait for the next key. ;; 'b' arrives. ;; ;; Lookup #2 for 'b' and find #3. Cancel the change in PREEDIT done ;; by the map actions of #2, and change MAP to #3. Perform the map ;; actions. Now, PREEDIT is "BB", CANDIDATE-LIST is (("BB" "Bb")), ;; CANDIDATE is "BB". As #3 has no sub transition maps and no branch ;; actions, change STATE to 'selection' and change MAP to #4, the root ;; map of 'selection'. As it has sub transition maps, and we have ;; consumed the key. Wait for the next key. ;; 'Right' arrives. ;; ;; Lookup #4 for 'Right' and find #5. Change MAP to #5. Perform the ;; map actions. Now PREEDIT is "Bb". As #5 has no sub transition ;; maps, no branch actions, no state to shift, change MAP to #4 (the ;; initial map of the current state 'selection'. ;; ;; As #4 has sub transition maps, and we have consumed the key. Wait ;; for the next key. ;; 'b' arrives. ;; ;; Lookup #4 for 'b' and fail. As #4 has no branch maps, no shift to ;; transit, change STATE to 'init', MAP to #0. ;; ;; As we have changed MAP to the root map of the initial map, ;; concatenate PREEDIT to PRODUCED, and reset PREEDIT to empty. Now ;; PRODUCED has "aBb". ;; ;; As we have not yet consumed the key 'b', handle it in MAP (#0). ;; ;; Lookup #0 for 'b' and find #2. Change MAP to #2. Perform the map ;; actions. Now, PREEDIT contains "B". As it has sub transition ;; maps, and we have consumed the key, wait for the next key. ;; 'a' arrives. Lookup #2 for 'a' and fail. As #2 has no branch ;; actions, no state to shift, Change MAP to #0, the root map of the ;; current state. ;; ;; As we have changed MAP to the root map of the initial map, ;; concatenate PREEDIT to PRODUCED, and reset PREEDIT to empty. Now ;; PRODUCED has "aBbb". ;; ;; As we have not yet consumed the key 'a', handle it in MAP (#0). ;; ;; Lookup #0 for 'a' and find #1. Change MAP to #1. Perform its map ;; actions. Now PREEDIT contains "a". As it has no sub transition ;; maps, no branch actions, no state to shift, change MAP to #0, the ;; root map of the current state. ;; ;; As we have changed MAP to the root map of the initial map, ;; concatenate PREEDIT to PRODUCED, and reset PREEDIT to empty. Now ;; PRODUCED has "aBbba". ;; Now we have consumed the key. As MAP has sub transition maps, wait ;; for the next key.