Rodin Keyboard: Difference between revisions

From Event-B
Jump to navigationJump to search
imported>Nicolas
imported>Nicolas
Line 55: Line 55:
Only search for first match, translate, then search for other matches in translated text repeatedly.
Only search for first match, translate, then search for other matches in translated text repeatedly.


=== Math Translation (regular or LaTeX style) ===
=== LaTeX Style Translation ===


Translate first longest substrings of matched part that equals a math input combination.
Translate first longest substrings of matched part that equals a math input combination.


Given text 'matched' to translate and caret position 'caret' (translated to fit inside 'matched', if needed):
Given text 'matched' to translate and caret position 'caret' (translated to 'matched', (== original caret position in whole text minus index of 'matched')):


<pre>
caretShift = 0 // will be added to the caret position after the translation, to determine new caret position
pending_translation = false
attempt = matched
while(attempt.length() > 0):
  attempt_has_caret = 0 <= caret <= attempt.length()
  possible_combinations = find_latex_combinations_with_prefix(attempt)
  foreach combo in possible_combinations by decreasing length:
    if combo.length() > attempt.length():
      if attempt_has_caret:
        return (translation = matched, pending_translation = true, caretShift = 0)
      else:
        // forget this possibility, the user is not typing it
        possible_combinations.remove(combo)
        continue
    if combo.length() == attempt.length(): // actually they are equal then
      translated_combo = translate(combo)
      if caret >= combo.length():
        caretShift += translated_combo.length() - combo.length()
      return (translation = translated_combo, pending_translation = true, caretShift = caretShift)
     
  attempt = remove_last_character(attempt)
return (translation = matched, pending_translation = false, caretShift = 0)
</pre>
=== Math Translation ===
Translate first longest substrings of matched parts that equal an input combination.
For instance, given '::=', translate into ':∈=', rather than ':≔'.
Other examples:
* '\lambdax' should translate into 'λx'.
* '\oftypeNAT' should translate into 'NAT' (in a math translation step, but NAT will be processed by text translation)
If there existed a translation for '/' into '÷' and a translation of '=/=' into '≠' (but no translation for '/=' or any '/*')
* '=/' with caret should stay unchanged because '=/' potentially starts a combination input, although '/' by itself is the longest combination input starting with '/'
* '=/' without caret translates to '=÷'
Given text 'text' to translate and caret position 'caret':


<pre>
<pre>
Line 112: Line 154:
      
      
</pre>
</pre>
=== Math Translation ===
Translate first longest substrings of matched parts that equal an input combination.
For instance, given '::=', translate into ':∈=', rather than ':≔'.
Other examples:
* '\lambdax' should translate into 'λx'.
* '\oftypeNAT' should translate into 'NAT' (in a math translation step, but NAT will be processed by text translation)
If there existed a translation for '/' into '÷' and a translation of '=/=' into '≠' (but no translation for '/=' or any '/*')
* '=/' with caret should stay unchanged because '=/' potentially starts a combination input, although '/' by itself is the longest combination input starting with '/'
* '=/' without caret translates to '=÷'
Given text 'text' to translate and caret position 'caret':


=== Text Translations ===
=== Text Translations ===

Revision as of 17:30, 21 May 2014

The Rodin Keyboard is an extensible keyboard for inputing mathematical formula (in Unicode). The Rodin keyboard provides the following facilities:

  • A ModifyListener (RodinModifyListener) that can be attached to a SWT widget. When the content of the widget is modified, the keyboard reacts and translate the content accordingly. Currently, RODIN Keyboard supports Text and StyledText widget.
  • An utility class Text2MathTranslator with a static method translate(String) for manually translating any string (or sub-part of a string) into mathematical formula.
  • An Eclipse View called Rodin Keyboard View which provides an text input area which will translate the input text into mathematical formula. This View can be found under category RODIN.

The Rodin Keyboard however does not contain any pre-defined translation rules for any mathematical symbols. Instead, this task is left for the developers who want to declare different "keyboards" corresponding to the mathematical language that they want to use. Moreover, different combinations can be used to enter the same mathematical symbols.

Currently, there are two keyboards available:

  • Standard keyboard for Event-B.
  • LaTeX-style keyboard for Event-B.

Specification

Definitions

  • Translator: function that transforms a (source chain, source caret position) into a (target chain, target caret position)
  • Token: character sequence in a chain that is of one lexical kind, as long as possible
  • Transformation Rule: specification of the modification of a token sequence into another token sequence
  • Combination: specification of a transformation from a single input token into another single output token
  • There are 3 kinds of lexical entities in a source chain:
    • spacings (space, tabulation, end of line, ...)
    • text symbols (identifier-like)
    • math symbols (all other symbols)

Functional Needs

  1. Translate a whole chain all at once
  2. Translate on the fly, characters being typed in continuously
  3. Maintain a coherent caret position
  4. Keep spacing unchanged
  5. Support statically contributed combinations (add)
  6. Support dynamically contributed combinations (check validity, add and remove, will be useful for theories)
    + maybe handle a scope ? could help when working with 2 projects that define the same combination input for different outputs
  7. Avoid non-termination cases (combinations that produce input of combinations)

Requirements

  1. The translator must check that combination input tokens are either text or math symbols
    A combination input is a valid text token if it matches
    [a-zA-Z0-9_]+
    (this excludes 'λ', '$', 'ℙ', … from identifiers)
    A combination input is a valid math token if it matches either:
    • [^a-zA-Z0-9_\s]+
    • \[a-zA-Z0-9_]+
      (LaTeX style token)
  2. The translator must check that combination output tokens are not a substring of any input token; this enforces one-pass termination of the translation
  3. The translator must not process a text token that has the caret over it
  4. The translator must process a math token (w or w/o the caret over it) that is the longest possible input combination
  5. Once a symbol has been translated, it can never be translated back into its original text form (even virtually during a translation)
  6. The translator must not process a language token, like ':∈' or '$POW' (but we may concede an exception for predicate variables for simplicity)

Algorithm

Translation must be performed in the following order:

  1. match and translate LaTeX style math tokens (doing it before text tokens, as it may contain translatable text substrings, and before regular math tokens because of first math token '\')
  2. match and translate regular math tokens
  3. match and translate text tokens

In order to match, use the above regular expressions for each category. Only search for first match, translate, then search for other matches in translated text repeatedly.

LaTeX Style Translation

Translate first longest substrings of matched part that equals a math input combination.

Given text 'matched' to translate and caret position 'caret' (translated to 'matched', (== original caret position in whole text minus index of 'matched')):


caretShift = 0 // will be added to the caret position after the translation, to determine new caret position
pending_translation = false

attempt = matched
while(attempt.length() > 0):
  attempt_has_caret = 0 <= caret <= attempt.length()
  possible_combinations = find_latex_combinations_with_prefix(attempt)

  foreach combo in possible_combinations by decreasing length:
    if combo.length() > attempt.length():
      if attempt_has_caret:
        return (translation = matched, pending_translation = true, caretShift = 0)
      else:
        // forget this possibility, the user is not typing it
        possible_combinations.remove(combo)
        continue
    if combo.length() == attempt.length(): // actually they are equal then
      translated_combo = translate(combo)
      if caret >= combo.length():
        caretShift += translated_combo.length() - combo.length()
      return (translation = translated_combo, pending_translation = true, caretShift = caretShift)
      
  attempt = remove_last_character(attempt)

return (translation = matched, pending_translation = false, caretShift = 0)

Math Translation

Translate first longest substrings of matched parts that equal an input combination. For instance, given '::=', translate into ':∈=', rather than ':≔'. Other examples:

  • '\lambdax' should translate into 'λx'.
  • '\oftypeNAT' should translate into 'NAT' (in a math translation step, but NAT will be processed by text translation)

If there existed a translation for '/' into '÷' and a translation of '=/=' into '≠' (but no translation for '/=' or any '/*')

  • '=/' with caret should stay unchanged because '=/' potentially starts a combination input, although '/' by itself is the longest combination input starting with '/'
  • '=/' without caret translates to '=÷'

Given text 'text' to translate and caret position 'caret':

translation = ""
caretShift = 0 // will be added to the caret position after the translation, to determine new caret position
pending_translation = false
i = 0
while i <= matched.length() {
  start_skip = i
  i = find_math_combination_input_start_skipping_language_symbols(matched,i)

  translation += matched.substring(start_skip, i)

  if i equals length of matched:
    return translation

  // i is now at start of a combination input
  start_combo = i
  i = skip_longest_math_combination_input(matched,i)
  
  combo = matched.substring(start_combo, i)

  combo_has_caret = start_combo <= caret <= i

  
  if combo is an incomplete combination input: // combination partially typed in
    if combo_has_caret:
      pending_translation = true
      translation += combo
    else:
      translation += matched[start_combo]
      i = start_combo + 1 // rewind and search for combinations inside combo
    continue

  // now combo is a complete combination input

  if combo is the longest possible combination input (i.e it is not a prefix of another combination input)
    or not combo_has_caret :
    translated_combo = translate(combo)
    translation += translated_combo
    if caret >= i:
      caretShift += translated_combo.length() - combo.length()
  else:
    // there exists a longer combination input
    // and the caret is over the combo: do not translate, let the user the possibility to
    // type the longer combination input, remember the pending translation status;
    // when a translation returns with a pending translation status, a caret listener will
    // be activated, that will trigger a new translation when the caret moves
    translation += combo
    pending_translation = true
    

Text Translations

Translate whole matched parts that equal an input combination.

  • do not translate within an identifier, for instance 'NATURAL' must remain unchanged by translation, rather than 'ℕURAL'
  • do not translate a match that has the caret, so as to enable inputting identifiers that contain a combination input