// Copyright (c) 2003-2005 The University of Wroclaw. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // 3. The name of the University may not be used to endorse or promote // products derived from this software without specific prior // written permission. // // THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``AS IS'' AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN // NO EVENT SHALL THE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // using Nemerle.Collections; namespace Nemerle.CSharp { public module Gotos { private mutable gotos : list [antlr.IToken] = []; private mutable labels : list [antlr.IToken] = []; public AddGoto ( label : antlr.IToken ) : void { gotos = label :: gotos; } public AddLabel ( label : antlr.IToken ) : void { labels = label :: labels; } public ClearGotos () : void { gotos = []; } public ClearLabels () : void { labels = []; } public Clear () : void { ClearLabels (); ClearGotos (); } public GetGotos : list [antlr.IToken] { get { gotos } } public GetLabels : list [antlr.IToken] { get { labels } } } public variant GotoOrLabel { | Goto { lst : list [STree]; } | Label { lst : list [STree]; } | Any } public partial variant STree { protected transform_gotos_to_conditionals () : STree { match(this) { | Statement ("WHEN",[when_,lp,cond,rp,maybe_goto]) as when_st=> if(maybe_goto is Statement ("GOTO",_)) when_st else Statement ("WHEN",[when_,lp,cond,rp,maybe_goto.transform_gotos_to_conditionals ()]) | Statement ("GOTO", goto :: _ ) as goto_st=> def ws = get_first_whitespace (goto); Statement("WHEN", [ make_token (ws,"when"), TokS(" ("), make_token("", " true ") , TokS (")"), goto_st]) | Statement ( name , children) => Statement (name , List.Map ( children , fun (x) { x.transform_gotos_to_conditionals () } ) ) | x => x } } private insert_goto (sts : list [STree],label : string) : option [list [STree]] { def negate_cond (cond) { | TokS (c) => TokS ("!" + c); | Tok (t) => make_token (ExtendedToken.getWhitespaces (t), "!" + ExtendedToken.getTextOnly (t)) | c => c } def insert_goto_aux (statements, crossed, begin) { match (statements) { | [] => ( None () ) | Statement ( "WHEN", [ Tok(when_tok) , _ , cond , _ , Statement ("GOTO" , [ _ , Tok(id) , _]) ] ) as goto_st :: tail => match (crossed) { | GotoOrLabel.Any => if ( ExtendedToken.getTextOnly (id) == label) insert_goto_aux (tail , GotoOrLabel.Goto ([cond]) , begin ) else insert_goto_aux (tail , GotoOrLabel.Any (), goto_st :: begin ) | GotoOrLabel.Goto (lst) => insert_goto_aux (tail , GotoOrLabel.Goto (goto_st :: lst) , begin ) | GotoOrLabel.Label (lst) => def ws = ExtendedToken.getWhitespaces (when_tok); if ( ExtendedToken.getTextOnly (id) == label) { def block = Statement ("BLOCK" , make_token (ws , "{") :: List.Rev(lst) + [make_token (ws , "}")]); def do_st = Statement("DO",[Tok (ExtendedToken (ws , "do")) , block , Tok ( ExtendedToken (ws , "while")) , TokS("(") , cond , TokS(")") , TokS(";")]); Some (List.Rev (begin) + [ do_st ] + tail) } else { insert_goto_aux (tail , GotoOrLabel.Label(goto_st :: lst) , begin) } } | (head : STree) :: tail => match (crossed) { | GotoOrLabel.Any => if (head.label_inside (label)) { def head_changed = head; //FIXME really chenge this insert_goto_aux (tail , GotoOrLabel.Label ([head_changed]), begin ) } else insert_goto_aux (tail , GotoOrLabel.Any (), head :: begin ) | GotoOrLabel.Goto (lst) => def ws = get_first_whitespace ([head]); if (head.label_inside (label)) { def head_changed = head; //FIXME really change this match(List.Rev(lst)) { | cond :: rest => def block = Statement ("BLOCK" , make_token (ws , "{") :: rest + [make_token (ws , "}")]); def new_when = Statement("WHEN", [ make_token (ws,"when") , TokS("(") , negate_cond(cond) , TokS(")") , block ]); Some (List.Rev (begin) + [ new_when ] + [head_changed] + tail) | [] => assert (false) } } else insert_goto_aux (tail , GotoOrLabel.Goto (head :: lst), begin ) | GotoOrLabel.Label (lst) => insert_goto_aux (tail , GotoOrLabel.Label (head :: lst) , begin ) } } } insert_goto_aux (sts, GotoOrLabel.Any (), []) } private eliminate_siblings (sts : list [STree],label : string) : option [list [STree]] { def negate_cond (cond) { | TokS (c) => TokS ("!" + c); | Tok (t) => make_token (ExtendedToken.getWhitespaces (t), "!" + ExtendedToken.getTextOnly (t)) | c => c } def eliminate_siblings_aux (statements, crossed, begin) { match (statements) { | [] => ( None () ) | Statement ( "WHEN", [ Tok(when_tok) , _ , cond , _ , Statement ("GOTO" , [ _ , Tok(id) , _]) ] ) as goto_st :: tail => def ws = ExtendedToken.getWhitespaces (when_tok); match (crossed) { | GotoOrLabel.Label (lst) => if ( ExtendedToken.getTextOnly (id) == label) { def block = Statement ("BLOCK" , make_token (ws , "{") :: List.Rev(lst) + [make_token (ws , "}")]); def do_st = Statement("DO",[Tok (ExtendedToken (ws , "do")) , block , Tok ( ExtendedToken (ws , "while")) , TokS("(") , cond , TokS(")") , TokS(";")]); Some (List.Rev (begin) + [ do_st ] + tail) } else { eliminate_siblings_aux (tail , GotoOrLabel.Label (goto_st :: lst) , begin) } | GotoOrLabel.Goto (lst) => eliminate_siblings_aux (tail , GotoOrLabel.Goto (goto_st :: lst) , begin ) | GotoOrLabel.Any => if ( ExtendedToken.getTextOnly (id) == label) eliminate_siblings_aux (tail , GotoOrLabel.Goto ([cond]) , begin ) else eliminate_siblings_aux (tail , GotoOrLabel.Any (), goto_st :: begin ) } | Statement ( "LABEL", [Tok(id), _ , _ ] ) as label_st :: tail => def ws = ExtendedToken.getWhitespaces (id); match (crossed) { | GotoOrLabel.Goto (lst) => if ( ExtendedToken.getTextOnly (id) == label) { match(List.Rev(lst)) { | cond :: rest => def block = Statement ("BLOCK" , make_token (ws , "{") :: rest + [make_token (ws , "}")]); def new_when = Statement("WHEN", [ make_token (ws,"when") , TokS("(") , negate_cond(cond) , TokS(")") , block ]); Some (List.Rev (begin) + [ new_when ] + [label_st] + tail) | [] => assert (false) } } else eliminate_siblings_aux (tail , GotoOrLabel.Goto (label_st :: lst) , begin ) | GotoOrLabel.Label (lst) => eliminate_siblings_aux (tail , GotoOrLabel.Label (label_st :: lst) , begin ) | GotoOrLabel.Any => if ( ExtendedToken.getTextOnly (id) == label) eliminate_siblings_aux (tail , GotoOrLabel.Label ([label_st]) , begin ) else eliminate_siblings_aux (tail , GotoOrLabel.Any (), label_st :: begin ) } | (head : STree):: tail => match (crossed) { | GotoOrLabel.Any => if( head.label_inside (label) && head.jump_inside ( Jump.Goto (label))) { match( head.go_inside (label)) { | None => eliminate_siblings_aux (tail , GotoOrLabel.Any (), head :: begin ) | Some (s) => Some (List.Rev (begin) + [s] + tail) } } else eliminate_siblings_aux (tail , GotoOrLabel.Any (), head :: begin ) | GotoOrLabel.Goto (lst) => eliminate_siblings_aux (tail , GotoOrLabel.Goto (head :: lst) , begin ) | GotoOrLabel.Label (lst) => eliminate_siblings_aux (tail , GotoOrLabel.Label (head :: lst) , begin ) } } } eliminate_siblings_aux (sts , GotoOrLabel.Any () , []); } private go_inside ( label : string) : option [STree] { def go_inside_aux( tree, f ) { match(tree) { | Statement ("BLOCK", xs) => def (lb, statements , rb) = cut_off_braces (xs); match( f (statements, label)) { | None => None () | Some (sts) => Some (Statement ("BLOCK", lb :: sts + [rb])) } | Statement ("CHECKED" as name, [w , block]) | Statement ("UNCHECKED" as name, [w , block]) => match(go_inside_aux ( block, do_the_stuff)) { | None => None () | Some (b) => Some( Statement ( name, [w , b] ) ) } | Statement ("IF", [if_tok , lp , expr , rp , block1, else_tok , block2]) => match(go_inside_aux ( block1, do_the_stuff)) { | None => match( go_inside_aux ( block2, do_the_stuff)) { | None => None () | Some (b) => Some( Statement ( "IF", [if_tok , lp , expr , rp , block1 , else_tok , b])) } | Some (b) => Some( Statement ( "IF", [if_tok , lp , expr , rp , b , else_tok , block2])) } | Statement ("LOCK" as name, [w , lp , expr , rp , block]) | Statement ("USING" as name, [w , lp , expr , rp , block]) | Statement ("WHILE" as name, [w , lp , expr , rp , block]) | Statement ("WHEN" as name, [w , lp , expr , rp , block]) => match(go_inside_aux ( block, do_the_stuff)) { | None => None () | Some (b) => Some( Statement ( name, [w , lp , expr , rp , b] ) ) } | Statement ("DO", do_tok :: block :: rest ) => match(block.go_inside ( label)) { | None => None () | Some (b) => Some( Statement ( "DO", do_tok :: b :: rest) ) } | _ => None () } } go_inside_aux (this, do_the_stuff) } private do_the_stuff (statements : list[STree] , label : string) : option [list [STree]] { mutable new_statements = match (insert_goto (statements, label)) { | None => statements | Some (sts) => sts }; new_statements = match (lift_goto (new_statements, label)) { | None => new_statements | Some (sts) => sts }; new_statements = match (eliminate_siblings (new_statements, label)) { | None => new_statements | Some (sts) => sts }; Some (new_statements) } private lift_goto (statements : list[STree], label : string) : option [list [STree]] { def lift_goto_aux (sts , acc , found) { match ( sts ) { | [] => None () | Statement ( "WHEN", [ _ , _ , _ , _ , Statement ("GOTO" , [ _ , Tok(id) , _]) ] ) as goto_st :: tail => if (ExtendedToken.getTextOnly (id) == label) None () else lift_goto_aux (tail, goto_st :: acc , found) | Statement ( "LABEL", [Tok(id), _ , _ ] ) as label_st :: tail => if (ExtendedToken.getTextOnly (id) == label) { if ( found) Some (List.Rev (acc) + (label_st :: tail)) else lift_goto_aux (tail, label_st :: acc , true) } else lift_goto_aux (tail, label_st :: acc , found) | head :: tail => if ( head.jump_inside (Jump.Goto (label)) ) { def head_el = head.eliminate_jump_2 (Jump.Goto (label)); if (found) Some (List.Rev (acc) + head_el + tail) else lift_goto_aux (tail, head_el + acc , true) } else lift_goto_aux (tail, head :: acc , found) } } lift_goto_aux (statements , [] , false) } private eliminate_goto () : STree { label_symbols.Clear (); mutable gotos_list = Gotos.GetGotos; mutable labels_list = Gotos.GetLabels; Gotos.Clear (); def s_labels_list = List.Map ( labels_list , ExtendedToken.getTextOnly ); def check_gotos (x) { def text = ExtendedToken.getTextOnly (x); when (! List.Member (s_labels_list , text )) Message.Error ("goto to non-existing label" , x); } List.Iter ( gotos_list , check_gotos ); def add_temp (x) { def gs = new_goto_symbol (); label_symbols.Add ( x, gs ); } List.Iter (List.Map (labels_list , ExtendedToken.getTextOnly ), add_temp); def (lb, statements , rb) = cut_off_braces (this); def loop (lst , statements) { match (lst) { | head :: tail => match( do_the_stuff (statements, head)) { | Some (sts) => loop (tail, sts) | None => assert (false); } | _ => statements } } def new_statements = loop (List.Map (gotos_list , ExtendedToken.getTextOnly ), List.Map ( statements , fun (x) {x.transform_gotos_to_conditionals ()} )); def fw = get_first_whitespace ( statements ); mutable inits = []; def add ( _ , i ) { inits = make_token (fw , "mutable " + i + " = false;") :: inits; } label_symbols.Iter ( add ); Statement ("BLOCK" , lb :: inits + new_statements + [rb]); } private label_inside (label : string) : bool { def loop(xs : list [STree]) : bool { | head :: tail => if(head.label_inside (label)) true else loop(tail) | _ => false } match(this) { | Statement ("LABEL", [Tok(id), _ , _ ] ) => ExtendedToken.getTextOnly (id) == label | Statement("BLOCK",xs) => loop(xs); | Statement("IF",[_,_,_,_,st1,_,st2]) => st1.label_inside (label) || st2.label_inside (label) | Statement("WHEN",[_,_,_,_,st]) => st.label_inside (label) | Statement("SWITCH",[_,_,_,_,Statement("SWITCH_BLOCK",xs)]) => loop(xs) | Statement("SWITCH_SECTION",[_,Statement("SWITCH_SECTION_STATEMENTS",xs)]) => loop(xs) | Statement("TRY",_ :: block :: _) | Statement("USING_STATEMENT",[_,_,_,_,block]) | Statement("LOCK",[_,_,_,_,block]) | Statement("CHECKED",[_,block]) | Statement("UNCHECKED",[_,block]) => block.label_inside (label) | Statement("DO", _ :: block :: _) => block.label_inside (label) | _ => false } } private make_token (ws : string, text : string) : STree { STree.Tok (ExtendedToken(ws,text)); } private new_goto_symbol () : string { new_symbol ("_goto_") } /* -- PRIVATE FIELDS ---------------------------------------------------- */ private static label_symbols : Nemerle.Collections.Hashtable [string , string] = Nemerle.Collections.Hashtable (); } }