00001 package gov.nasa.arc.ase.ltl;
00002
00003
00004
00005
00006 import java.util.*;
00007
00008 public class Formula implements Comparable {
00009 private char content;
00010 private boolean literal;
00011 private Formula left;
00012 private Formula right;
00013 private int id;
00014 private int untils_index;
00015 private int rightOfUntil_index;
00016 private String name;
00017
00018 public static Vector untils = new Vector();
00019 public static Formula[] until_forms;
00020
00021 private static int nId = 0;
00022
00023 private static final int P_ALL = 0;
00024 private static final int P_OR = 1;
00025 private static final int P_AND = 2;
00026 private static final int P_UNTIL = 3;
00027 private static final int P_WUNTIL = 3;
00028 private static final int P_RELEASE = 4;
00029 private static final int P_WRELEASE = 4;
00030 private static final int P_IMPLIES = 5;
00031 private static final int P_NOT = 6;
00032 private static final int P_NEXT = 6;
00033 private static final int P_ALWAYS = 6;
00034 private static final int P_EVENTUALLY = 6;
00035
00036
00037 public static class EndOfInputException extends Exception {
00038 }
00039
00040
00041 private static Hashtable ht = new Hashtable();
00042
00043 private static class Input {
00044 private StringBuffer sb;
00045
00046
00047 public Input(String str) {
00048 sb = new StringBuffer(str);
00049 }
00050
00051
00052 public char get() throws EndOfInputException {
00053 try {
00054 return sb.charAt(0);
00055 } catch(StringIndexOutOfBoundsException e) {
00056 throw new EndOfInputException();
00057 }
00058 }
00059
00060
00061 public void skip() throws EndOfInputException {
00062 try {
00063 sb.deleteCharAt(0);
00064 } catch(StringIndexOutOfBoundsException e) {
00065 throw new EndOfInputException();
00066 }
00067 }
00068 }
00069
00070 private static Hashtable matches = new Hashtable();
00071
00072 private Formula(char c, boolean l, Formula sx, Formula dx, String n) {
00073 id = nId++;
00074 content = c;
00075 literal = l;
00076 left = sx;
00077 right = dx;
00078 name = n;
00079 rightOfUntil_index = -1;
00080 untils_index = -1;
00081 }
00082 public void addLeft (Formula l)
00083 {
00084 left = l;
00085 }
00086 private void addMatch(String name, Formula expr) {
00087 matches.put(name, expr);
00088 }
00089 public void addRight (Formula r)
00090 {
00091 right = r;
00092 }
00093 private static Formula Always(Formula f) {
00094 return unique(new Formula('V', false, False(), f, null));
00095 }
00096 private static Formula And(Formula sx, Formula dx) {
00097 if(sx.id < dx.id)
00098 return unique(new Formula('A', false, sx, dx, null));
00099 else
00100 return unique(new Formula('A', false, dx, sx, null));
00101 }
00102 private void clearMatches() {
00103 matches = new Hashtable();
00104 }
00105 public int compareTo(Object f)
00106 {
00107 Formula ff = (Formula) f;
00108 return (this.id - ff.id);
00109
00110
00111 }
00112 private static Formula Eventually(Formula f) {
00113 return unique(new Formula('U', false, True(), f, null));
00114 }
00115 private static Formula False() {
00116 return unique(new Formula('f', true, null, null, null));
00117 }
00118 public int get_rightOfUntils_index()
00119 {
00120 return (rightOfUntil_index);
00121 }
00122 public int get_untils_index()
00123 {
00124 return (untils_index);
00125 }
00126 public char getContent ()
00127 {
00128 return content;
00129 }
00130 private Formula getMatch(String name) {
00131 return (Formula)matches.get(name);
00132 }
00133 public String getName ()
00134 {
00135 return name;
00136 }
00137 public Formula getNext()
00138 {
00139 switch (content)
00140 {
00141 case 'U':
00142 case 'W':
00143 return this;
00144 case 'V':
00145 return this;
00146 case 'O':
00147 return null;
00148 default:
00149
00150 return null;
00151 }
00152 }
00153 public Formula getSub1 ()
00154 {
00155 if (content == 'V')
00156 return right;
00157 else
00158 return left;
00159 }
00160 public Formula getSub2 ()
00161 {
00162 if (content == 'V')
00163 return left;
00164 else
00165 return right;
00166 }
00167 private static Formula Implies(Formula sx, Formula dx) {
00168 return Or(Not(sx), dx);
00169 }
00170 public boolean is_literal()
00171 {
00172 return (literal);
00173 }
00174 public static boolean is_reserved_char(char ch)
00175 {
00176 switch (ch)
00177 {
00178
00179
00180 case 'U':
00181 case 'V':
00182 case 'W':
00183 case 'M':
00184 case 'X':
00185 case ' ':
00186 case '<':
00187 case '>' :
00188 case '(':
00189 case ')':
00190 case '[':
00191 case ']':
00192 case '-':
00193
00194 return true;
00195 default :
00196 return false;
00197 }
00198 }
00199 public boolean is_right_of_until()
00200 {
00201 return (rightOfUntil_index >=0);
00202 }
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227 public boolean is_special_case_of_V (TreeSet check_against)
00228 {
00229 Formula form = (Release(False(), this));
00230 if (check_against.contains(form))
00231 return true;
00232 else
00233 return false;
00234 }
00235 public boolean is_synt_implied (TreeSet old, TreeSet next)
00236 {
00237 if (this.getContent() == 't')
00238 return true;
00239 if (old.contains(this))
00240 return true;
00241
00242
00243 if (! is_literal())
00244 {
00245 Formula form1 = this.getSub1();
00246 Formula form2 = this.getSub2();
00247 Formula form3 = this.getNext();
00248
00249 boolean condition1, condition2, condition3;
00250
00251 if (form2 != null)
00252 condition2 = form2.is_synt_implied(old, next);
00253 else
00254 condition2 = true;
00255 if (form1 != null)
00256 condition1 = form1.is_synt_implied(old, next);
00257 else
00258 condition1 = true;
00259 if (form3 != null) {
00260 if (next != null)
00261 condition3 = next.contains(form3);
00262 else
00263 condition3 = false;
00264 }
00265 else
00266 condition3 = true;
00267
00268 switch (getContent())
00269 {
00270
00271 case 'U':
00272 case 'W':
00273 case 'O':
00274 return (condition2 || (condition1 && condition3));
00275 case 'V':
00276 return ( (condition1 && condition2)
00277 || (condition1 && condition3)
00278 );
00279 case 'X': if (form1!=null) {
00280 if (next != null)
00281 return (next.contains(form1));
00282 else
00283 return false;
00284 } else
00285 return (true);
00286 case 'A': return (condition2 && condition1);
00287 default:
00288 System.out.println("Default case of switch at Form.synt_implied");
00289 return false;
00290
00291 }
00292 }
00293 else
00294 {
00295 return false;
00296 }
00297 }
00298 private boolean match(Formula rule) {
00299 if(rule.content == 'p') {
00300 Formula match = getMatch(rule.name);
00301
00302 if(match == null) {
00303 addMatch(rule.name, this);
00304 return true;
00305 }
00306
00307 return match == this;
00308 }
00309
00310 if(rule.content != content) return false;
00311
00312 Hashtable saved = (Hashtable)matches.clone();
00313
00314 switch(content) {
00315 case 'A':
00316 case 'O':
00317 if(left.match(rule.left) && right.match(rule.right))
00318 return true;
00319
00320 matches = saved;
00321 if(right.match(rule.left) && left.match(rule.right))
00322 return true;
00323
00324 matches = saved;
00325 return false;
00326
00327 case 'U':
00328 case 'V':
00329 case 'W':
00330 if(left.match(rule.left) && right.match(rule.right))
00331 return true;
00332
00333 matches = saved;
00334 return false;
00335
00336 case 'X':
00337 case 'N':
00338 if(left.match(rule.left))
00339 return true;
00340
00341 matches = saved;
00342 return false;
00343
00344 case 't':
00345 case 'f':
00346 return true;
00347 }
00348
00349 throw new RuntimeException("code should not be reached");
00350 }
00351 public Formula negate ()
00352 {
00353 return Not(this);
00354 }
00355 private static Formula Next(Formula f) {
00356 return unique(new Formula('X', false, f, null, null));
00357 }
00358 private static Formula Not(Formula f) {
00359 if (f.literal)
00360 switch (f.content)
00361 {
00362 case 't': return False();
00363 case 'f': return True();
00364 case 'N': return f.left;
00365 default : return unique(new Formula('N', true, f, null, null));
00366 }
00367
00368
00369
00370
00371 switch(f.content) {
00372 case 'A': return Or(Not(f.left), Not(f.right));
00373 case 'O': return And(Not(f.left), Not(f.right));
00374 case 'U': return Release(Not(f.left), Not(f.right));
00375 case 'V': return Until(Not(f.left), Not(f.right));
00376 case 'W': return WRelease(Not(f.left), Not(f.right));
00377
00378 case 'N': return f.left;
00379 case 'X': return Next(Not(f.left));
00380 default: throw new ParserInternalError();
00381 }
00382 }
00383 private static Formula Or(Formula sx, Formula dx) {
00384 if(sx.id < dx.id)
00385 return unique(new Formula('O', false, sx, dx, null));
00386 else
00387 return unique(new Formula('O', false, dx, sx, null));
00388 }
00389 static private Formula parse(Input i, int precedence) throws ParseErrorException {
00390 try {
00391 Formula formula;
00392 char ch;
00393
00394
00395 while(i.get() == ' ') i.skip();
00396
00397
00398 switch(ch = i.get()) {
00399 case '/':
00400 case '\\':
00401 case 'U':
00402 case 'W':
00403 case 'V':
00404 case 'M':
00405 case ')':
00406 throw new ParseErrorException("invalid character: " + ch);
00407
00408
00409 case '!':
00410 i.skip();
00411 formula = Not(parse(i, P_NOT));
00412 break;
00413
00414
00415 case 'X':
00416 i.skip();
00417 formula = Next(parse(i, P_NEXT));
00418 break;
00419
00420
00421 case '[':
00422 i.skip();
00423 if(i.get() != ']')
00424 throw new ParseErrorException("expected ]");
00425 i.skip();
00426 formula = Always(parse(i, P_ALWAYS));
00427 break;
00428
00429
00430 case '<':
00431 i.skip();
00432 if(i.get() != '>')
00433 throw new ParseErrorException("expected >");
00434 i.skip();
00435 formula = Eventually(parse(i, P_EVENTUALLY));
00436 break;
00437
00438
00439 case '(':
00440 i.skip();
00441 formula = parse(i, P_ALL);
00442 if(i.get() != ')')
00443 throw new ParseErrorException("invalid character: " + ch);
00444 i.skip();
00445 break;
00446
00447
00448 case '"':
00449 StringBuffer sb = new StringBuffer();
00450
00451 i.skip();
00452 while((ch = i.get()) != '"') {
00453 sb.append(ch);
00454 i.skip();
00455 }
00456 i.skip();
00457
00458 formula = Proposition(sb.toString());
00459 break;
00460
00461
00462 default:
00463
00464 if (Character.isJavaIdentifierStart(ch))
00465 {
00466 StringBuffer sbf = new StringBuffer();
00467
00468 sbf.append(ch);
00469 i.skip();
00470
00471 try
00472 {
00473 while(Character.isJavaIdentifierPart(ch = i.get())&&
00474 (!Formula.is_reserved_char(ch)) )
00475 {
00476 sbf.append(ch);
00477 i.skip();
00478
00479 }
00480 } catch(EndOfInputException e) {
00481
00482 }
00483 String id = sbf.toString();
00484 if (id.equals("true"))
00485 {
00486 formula = True();
00487 } else if(id.equals("false"))
00488 {
00489 formula = False();
00490 } else
00491 {
00492 formula = Proposition(sbf.toString());
00493 }
00494
00495 }
00496 else
00497 throw new ParseErrorException("invalid character: " + ch);
00498 break;
00499 }
00500
00501
00502 try {
00503 while(i.get() == ' ') i.skip();
00504 ch = i.get();
00505 } catch(EndOfInputException e) {
00506 return formula;
00507 }
00508
00509
00510 while(true) {
00511 switch(ch) {
00512 case '/':
00513 if (precedence > P_AND) return formula;
00514 i.skip();
00515 if(i.get() != '\\')
00516 throw new ParseErrorException("expected \\");
00517 i.skip();
00518 formula = And(formula, parse(i, P_AND));
00519 break;
00520
00521
00522 case '\\':
00523 if (precedence > P_OR) return formula;
00524 i.skip();
00525 if(i.get() != '/')
00526 throw new ParseErrorException("expected /");
00527 i.skip();
00528 formula = Or(formula, parse(i, P_OR));
00529 break;
00530
00531
00532 case 'U':
00533 if (precedence > P_UNTIL) return formula;
00534 i.skip();
00535 formula = Until(formula, parse(i, P_UNTIL));
00536 break;
00537
00538 case 'W':
00539 if (precedence > P_WUNTIL) return formula;
00540 i.skip();
00541 formula = WUntil(formula, parse(i, P_WUNTIL));
00542 break;
00543
00544 case 'V':
00545 if (precedence > P_RELEASE) return formula;
00546 i.skip();
00547 formula = Release(formula, parse(i, P_RELEASE));
00548 break;
00549
00550 case 'M':
00551 if (precedence > P_WRELEASE) return formula;
00552 i.skip();
00553 formula = WRelease(formula, parse(i, P_WRELEASE));
00554 break;
00555
00556
00557 case '-':
00558 if (precedence > P_IMPLIES) return formula;
00559 i.skip();
00560 if(i.get() != '>')
00561 throw new ParseErrorException("expected >");
00562 i.skip();
00563 formula = Implies(formula, parse(i, P_IMPLIES));
00564 break;
00565
00566
00567 case ')':
00568 return formula;
00569
00570
00571 case '!':
00572 case 'X':
00573 case '[':
00574 case '<':
00575 case '(':
00576 default:
00577 throw new ParseErrorException("invalid character: " + ch);
00578 }
00579
00580
00581 try {
00582 while(i.get() == ' ') i.skip();
00583 ch = i.get();
00584 } catch(EndOfInputException e) {
00585 break;
00586 }
00587 }
00588 return formula;
00589 } catch(EndOfInputException e) {
00590 throw new ParseErrorException("unexpected end of input");
00591 }
00592 }
00593 static public Formula parse(String str) throws ParseErrorException {
00594 Input i = new Input(str);
00595 return parse(i, P_ALL);
00596 }
00597 public int processRightUntils()
00598 { int acc_sets = 0;
00599
00600
00601 if (getContent() == 'U')
00602 {
00603 untils.add(this);
00604 this.untils_index = right.rightOfUntil_index = untils.size() -1;
00605
00606
00607 }
00608
00609 if (left != null) left.processRightUntils();
00610 if (right != null) right.processRightUntils();
00611
00612
00613
00614 return untils.size();
00615 }
00616 private static Formula Proposition(String name) {
00617 return unique(new Formula('p', true, null, null, name));
00618 }
00619 private static Formula Release(Formula sx, Formula dx) {
00620 return unique(new Formula('V', false, sx, dx, null));
00621 }
00622 public static void reset_static()
00623 {
00624 untils = new Vector();
00625 until_forms = null;
00626 }
00627 private Formula rewrite() {
00628 if(content == 'p')
00629 return getMatch(name);
00630
00631 switch(content) {
00632 case 'A': return And(left.rewrite(), right.rewrite());
00633 case 'O': return Or(left.rewrite(), right.rewrite());
00634 case 'U': return Until(left.rewrite(), right.rewrite());
00635 case 'V': return Release(left.rewrite(), right.rewrite());
00636 case 'W': return WUntil(left.rewrite(), right.rewrite());
00637 case 'X': return Next(left.rewrite());
00638 case 'N': return Not(left.rewrite());
00639 case 't': return True();
00640 case 'f': return False();
00641 }
00642
00643 throw new RuntimeException("code should not be reached");
00644 }
00645 public Formula rewrite(Formula rule, Formula rewritten) {
00646 switch(content) {
00647 case 'A':
00648 case 'O':
00649 case 'U':
00650 case 'V':
00651 case 'W':
00652 left = left.rewrite(rule, rewritten);
00653 right = right.rewrite(rule, rewritten);
00654 break;
00655
00656 case 'X':
00657 case 'N':
00658 left = left.rewrite(rule, rewritten);
00659 break;
00660
00661 case 't':
00662 case 'f':
00663 case 'p':
00664 break;
00665 }
00666
00667 if(match(rule)) {
00668 Formula expr = rewritten.rewrite();
00669
00670 clearMatches();
00671
00672 return expr;
00673 }
00674
00675 clearMatches();
00676
00677 return this;
00678 }
00679 public int size() {
00680 switch(content) {
00681 case 'A':
00682 case 'O':
00683 case 'U':
00684 case 'V':
00685 case 'W':
00686 return left.size() + right.size() + 1;
00687
00688 case 'X':
00689 case 'N':
00690 return left.size() + 1;
00691
00692 default:
00693 return 0;
00694 }
00695 }
00696 public String toString() {
00697 switch(content) {
00698 case 'A': return "( " + left.toString() + " /\\ " + right.toString() + " )";
00699 case 'O': return "( " + left.toString() + " \\/ " + right.toString() + " )";
00700 case 'U': return "( " + left.toString() + " U " + right.toString() + " )";
00701 case 'V': return "( " + left.toString() + " V " + right.toString() + " )";
00702 case 'W': return "( " + left.toString() + " W " + right.toString() + " )";
00703
00704 case 'X': return "( () " + left.toString() + " )";
00705 case 'N': return "( ! " + left.toString() + " )";
00706 case 't': return "( true )";
00707 case 'f': return "( false )";
00708 case 'p': return "( \"" + name + "\" )";
00709 default: return new Character(content).toString();
00710 }
00711 }
00712 public String toString(boolean exprId) {
00713 if(!exprId) return toString();
00714
00715
00716 switch(content) {
00717 case 'A': return "( " + left.toString(true) + " /\\ " + right.toString(true) + " )[" + id + "]";
00718 case 'O': return "( " + left.toString(true) + " \\/ " + right.toString(true) + " )[" + id + "]";
00719 case 'U': return "( " + left.toString(true) + " U " + right.toString(true) + " )[" + id + "]";
00720 case 'V': return "( " + left.toString(true) + " V " + right.toString(true) + " )[" + id + "]";
00721 case 'W': return "( " + left.toString(true) + " W " + right.toString(true) + " )[" + id + "]";
00722
00723 case 'X': return "( () " + left.toString(true) + " )[" + id + "]";
00724 case 'N': return "( ! " + left.toString(true) + " )[" + id + "]";
00725 case 't': return "( true )[" + id + "]";
00726 case 'f': return "( false )[" + id + "]";
00727 case 'p': return "( \"" + name + "\" )[" + id + "]";
00728 default: return "( " + content + " )[" + id + "]";
00729 }
00730 }
00731 private static Formula True() {
00732 return unique(new Formula('t', true, null, null, null));
00733 }
00734 private static Formula unique(Formula f) {
00735 String s = f.toString();
00736
00737
00738 if(ht.containsKey(s))
00739 return (Formula)ht.get(s);
00740
00741
00742 ht.put(s, f);
00743 return f;
00744 }
00745 private static Formula Until(Formula sx, Formula dx) {
00746 return unique(new Formula('U', false, sx, dx, null));
00747 }
00748 private static Formula WRelease(Formula sx, Formula dx) {
00749 return unique(new Formula('U', false, dx, And(sx, dx), null));
00750 }
00751 private static Formula WUntil(Formula sx, Formula dx) {
00752 return unique(new Formula('W', false, sx, dx, null));
00753 }
00754 }