1 package isac.gui.mawen.editor
3 import isac.gui.mawen.syntax.Ast._
4 import isac.gui.mawen.syntax.Ast
5 import java.awt.event.KeyEvent
8 * Edit a formulas listening to key events
9 * according to a state-transition-system
10 * described in mmahringer Fig.TODO.TODOWN.
14 def parse(c: AstContainer, inputCode: Int) : Unit = {
15 val cursorAst = AstInfoUtil.FindCursor(c.getAst())
16 val cursorAstElem = AstInfoUtil.AstOfCursor(cursorAst)
17 findState(c, cursorAst, inputCode) match {
18 case Some(ast) => c.setAst(TransformAstUtil.Update(c.getAst(), cursorAst, TransformAstUtil.ReplaceCursor(ast)))
22 def findState(c: AstContainer, cursorAst: Ast, inputCode: Int) : Option[Ast] = cursorAst match {
23 case Appl(List(Variable(str), Constant("CURSOR"))) if str forall Character.isDigit => numberState(AstInfoUtil.AstOfCursor(cursorAst), inputCode) match {
24 case Some(ast) => Some(ast)
25 case None => startState(c, inputCode, cursorAst)
27 case Appl(List(a, Constant("CURSOR"))) => identState(AstInfoUtil.AstOfCursor(cursorAst), inputCode) match {
28 case Some(ast) => Some(ast)
29 case None => startState(c, inputCode, cursorAst)
30 }case Appl(List(Constant("CURSOR"), Constant("GAP"))) => identState(AstInfoUtil.AstOfCursor(cursorAst), inputCode)
31 case _ => startState(c, inputCode, cursorAst)
34 def startState(c: AstContainer, inputCode: Int, cursorAst: Ast) : Option[Ast] = {
36 if (isActionCode(inputCode)) {
38 return ActionState(c, -inputCode, cursorAst)
39 } else if (isDelim(inputCode) ) {
40 return delimerState(c, inputCode.toChar , cursorAst)
44 def identState(cursorAstElem: Ast, inputCode: Int) = cursorAstElem match {
45 case Variable(str) if isIdentifier(inputCode) || isNumber(inputCode) => Some(Appl(List(Variable(str + inputCode.toChar), Constant("CURSOR"))))
46 case Constant("GAP") if isIdentifier(inputCode) || isNumber(inputCode) => Some(Appl(List(Variable("" + inputCode.toChar), Constant("CURSOR"))))
47 case Variable(str) if inputCode == -KeyEvent.VK_BACK_SPACE && str.length() == 1 => Some(Appl(List(Constant("CURSOR"), Constant("GAP"))))
48 case Variable(str) if inputCode == -KeyEvent.VK_BACK_SPACE => Some(Appl(List(Variable(str.slice(0, str.length - 1)), Constant("CURSOR"))))
51 def numberState(cursorAstElem: Ast, inputCode: Int) : Option[Ast] = cursorAstElem match {
52 case Variable(str) if (isNumber(inputCode.toChar)) => Some(Appl(List(Variable(str + inputCode.toChar), Constant("CURSOR"))))
53 case Constant("GAP") if isIdentifier(inputCode) || isNumber(inputCode) => Some(Appl(List(Variable("" + inputCode.toChar), Constant("CURSOR"))))
54 case Variable(str) if inputCode == -KeyEvent.VK_BACK_SPACE && str.length() == 1 => Some(Appl(List(Constant("CURSOR"), Constant("GAP"))))
55 case Variable(str) if inputCode == -KeyEvent.VK_BACK_SPACE => Some(Appl(List(Variable(str.slice(0, str.length - 1)), Constant("CURSOR"))))
61 def ActionState(c: AstContainer, code: Int, cursorAst: Ast) : Option[Ast] = code match {
62 case KeyEvent.VK_RIGHT => TransformAstUtil.CursorNextChild(c); None
63 case KeyEvent.VK_LEFT => TransformAstUtil.CursorPrevChild(c); None
64 case KeyEvent.VK_UP => TransformAstUtil.CursorParent(c); None
65 case KeyEvent.VK_DOWN => TransformAstUtil.CursorChild(c); None
66 case KeyEvent.VK_ENTER => cursorAst match {
67 case Appl(List(Constant("CURSOR"), a)) => Some(a)
68 case Appl(List(a, Constant("CURSOR"))) => Some(a)
71 case KeyEvent.VK_DELETE => Some(Appl(List(Constant("CURSOR"),Constant("GAP"))))
72 case KeyEvent.VK_F2 => {
73 val b = AstInfoUtil.FindBox(c.getAst())
78 c.setAst(TransformAstUtil.Update(c.getAst(), b, (a) => a match {
79 case Appl(List(Constant(str), ast)) if str.startsWith("BOX") => Appl(List(Constant("CURSOR"), ast))
84 def delimerState(c: AstContainer, input: Character, cursorAst: Ast) : Option[Ast] = AstInfoUtil.AstOfCursor(cursorAst) match {
85 case Variable(str) if input == '(' && isLongDelim(str)
86 => Some(ReplaceFirstGap(
87 Settings.OperatorToAst(str),
88 Appl(List(Constant("CURSOR"), Constant("GAP")))))
90 => Some(ReplaceFirstGap(
92 Settings.OperatorToAst(input.toString()),
94 Appl(List(Constant("CURSOR"), Constant("GAP")))))
96 => Some(ReplaceFirstGap(
98 Settings.OperatorToAst(input.toString()),
100 Appl(List(Constant("CURSOR"), Constant("GAP")))))
101 case Constant("GAP") => Some(Settings.OperatorToAst(input.toString()))
102 case Constant(str) => {
103 val operatorParams = AstInfoUtil.Parent(c.getAst(), cursorAst).asInstanceOf[Appl].name.tail
104 var newOperatorAst = Settings.OperatorToAst(input.toString())
105 for(a <- operatorParams) {
106 newOperatorAst = ReplaceFirstGap(newOperatorAst, a)
108 newOperatorAst = newOperatorAst match {
109 case Appl(c::a) => Appl(Appl(List(c, Constant("CURSOR"))) :: a)
110 case _ => Constant("GAP")
112 c.setAst(TransformAstUtil.Update(c.getAst(), AstInfoUtil.Parent(c.getAst(), cursorAst), (a) => newOperatorAst))
122 def isNumber(ch: Integer) : Boolean = ch >= '0' && ch <= '9'
123 def isIdentifier(ch: Integer) : Boolean = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
124 def isDelim(ch: Integer) : Boolean= List('+', '-', '*', '/', '(', '=').contains(ch)
125 def isActionCode(code: Integer) : Boolean = code == -KeyEvent.VK_ENTER ||
126 code == -KeyEvent.VK_LEFT ||
127 code == -KeyEvent.VK_UP ||
128 code == -KeyEvent.VK_RIGHT ||
129 code == -KeyEvent.VK_DOWN ||
130 code == -KeyEvent.VK_DELETE ||
131 code == -KeyEvent.VK_F2
132 //TODOWN rename to isLongOp
133 def isLongDelim(ch: String) : Boolean= Settings.layout.keys.filter(x => x.length() > 1).toList.contains(ch)
136 def OperatorToAst(ch: Character) : Ast = Settings.OperatorToAst(ch.toString())
141 def ReplaceFirstGap(ast: Ast, replace: Ast) : Ast = ast match {
142 case Appl(Constant("GAP") :: asts) => Appl(replace :: asts)
143 case Appl(a :: Constant("GAP") :: asts) => Appl(a :: replace :: asts)
144 case Appl(Appl(iasts):: oasts) => Appl(ReplaceFirstGap(Appl(iasts), replace) :: oasts)
145 case Appl(a :: asts) => Appl(a :: ReplaceFirstGap(Appl(asts), replace).asInstanceOf[Appl].name)