1//
2//  ANTLRTreeRewriter.m
3//  ANTLR
4//
5//  Created by Alan Condit on 6/17/10.
6// [The "BSD licence"]
7// Copyright (c) 2010 Alan Condit
8// All rights reserved.
9//
10// Redistribution and use in source and binary forms, with or without
11// modification, are permitted provided that the following conditions
12// are met:
13// 1. Redistributions of source code must retain the above copyright
14//    notice, this list of conditions and the following disclaimer.
15// 2. Redistributions in binary form must reproduce the above copyright
16//    notice, this list of conditions and the following disclaimer in the
17//    documentation and/or other materials provided with the distribution.
18// 3. The name of the author may not be used to endorse or promote products
19//    derived from this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32#import "ANTLRTreeRewriter.h"
33#import "ANTLRCommonTreeNodeStream.h"
34#import "ANTLRTreeRuleReturnScope.h"
35#import "ANTLRCommonTreeAdaptor.h"
36#import "ANTLRTreeVisitor.h"
37
38@implementation ANTLRfptr
39
40+ (ANTLRfptr *)newANTLRfptrWithRule:(SEL)aRuleAction withObject:(id)anObject
41{
42    return [[ANTLRfptr alloc] initWithRule:aRuleAction withObject:(id)anObject];
43}
44
45-initWithRule:(SEL)aRuleAction withObject:(id)anObject
46{
47    if ((self = [super init]) != nil) {
48        actor = anObject;
49        ruleSEL = aRuleAction;
50    }
51    return self;
52}
53
54- (id)rule
55{
56	if ( [actor respondsToSelector:ruleSEL] )
57		return [actor performSelector:ruleSEL];
58    else
59        @throw [ANTLRRuntimeException newException:@"Unknown Rewrite exception"];
60    return nil;
61}
62
63@synthesize actor;
64@synthesize ruleSEL;
65@end
66
67@implementation ANTLRTreeRewriter
68
69+ (ANTLRTreeRewriter *) newANTLRTreeRewriter:(id<ANTLRTreeNodeStream>)anInput
70{
71    return [[ANTLRTreeRewriter alloc] initWithStream:anInput State:[ANTLRRecognizerSharedState newANTLRRecognizerSharedState]];
72}
73
74+ (ANTLRTreeRewriter *) newANTLRTreeRewriter:(id<ANTLRTreeNodeStream>)anInput State:(ANTLRRecognizerSharedState *)aState
75{
76    return [[ANTLRTreeRewriter alloc] initWithStream:anInput State:aState];
77}
78
79- (id)initWithStream:(id<ANTLRTreeNodeStream>)anInput
80{
81    SEL aRuleSel;
82
83    if ((self = [super initWithStream:anInput]) != nil) {
84        showTransformations = NO;
85        state = [[ANTLRRecognizerSharedState newANTLRRecognizerSharedState] retain];
86        originalAdaptor = [input getTreeAdaptor];
87        if ( originalAdaptor ) [originalAdaptor retain];
88        originalTokenStream = [input getTokenStream];        
89        if ( originalTokenStream ) [originalTokenStream retain];
90        aRuleSel = @selector(topdown);
91        topdown_fptr = [ANTLRfptr newANTLRfptrWithRule:(SEL)aRuleSel withObject:self];
92        aRuleSel = @selector(bottomup);
93        bottomup_ftpr = [ANTLRfptr newANTLRfptrWithRule:(SEL)aRuleSel withObject:self];        
94    }
95    return self;
96}
97
98- (id)initWithStream:(id<ANTLRTreeNodeStream>)anInput State:(ANTLRRecognizerSharedState *)aState
99{
100    SEL aRuleSel;
101    
102    if ((self = [super initWithStream:anInput]) != nil) {
103        showTransformations = NO;
104        state = aState;
105        if ( state ) [state retain];
106        originalAdaptor = [input getTreeAdaptor];
107        if ( originalAdaptor ) [originalAdaptor retain];
108        originalTokenStream = [input getTokenStream];        
109        if ( originalTokenStream ) [originalTokenStream retain];
110        aRuleSel = @selector(topdown);
111        topdown_fptr = [ANTLRfptr newANTLRfptrWithRule:(SEL)aRuleSel withObject:self];
112        aRuleSel = @selector(bottomup);
113        bottomup_ftpr = [ANTLRfptr newANTLRfptrWithRule:(SEL)aRuleSel withObject:self];        
114    }
115    return self;
116}
117
118- (void) dealloc
119{
120#ifdef DEBUG_DEALLOC
121    NSLog( @"called dealloc in ANTLRTreeRewriter" );
122#endif
123	if ( state ) [state release];
124	if ( originalAdaptor ) [originalAdaptor release];
125	if ( originalTokenStream ) [originalTokenStream release];
126	[super dealloc];
127}
128
129- (id) applyOnce:(ANTLRCommonTree *)t Rule:(ANTLRfptr *)whichRule
130{
131    if ( t == nil ) return nil;
132    @try {
133        // share TreeParser object but not parsing-related state
134        state = [ANTLRRecognizerSharedState newANTLRRecognizerSharedState];
135        input = [ANTLRCommonTreeNodeStream newANTLRCommonTreeNodeStream:(ANTLRCommonTreeAdaptor *)originalAdaptor Tree:t];
136        [(ANTLRCommonTreeNodeStream *)input setTokenStream:originalTokenStream];
137        [self setBacktrackingLevel:1];
138        ANTLRTreeRuleReturnScope *r = [(ANTLRfptr *)whichRule rule];
139        [self setBacktrackingLevel:0];
140        if ( [self getFailed] )
141            return t;
142        if ( showTransformations &&
143            r != nil && !(t == r.start) && r.start != nil ) {
144            [self reportTransformation:t Tree:r.start];
145        }
146        if ( r != nil && r.start != nil )
147            return r.start;
148        else
149            return t;
150    }
151    @catch (ANTLRRecognitionException *e) {
152        return t;
153    }
154    return t;
155}
156
157- (id) applyRepeatedly:(ANTLRCommonTree *)t Rule:(ANTLRfptr *)whichRule
158{
159    BOOL treeChanged = true;
160    while ( treeChanged ) {
161        ANTLRTreeRewriter *u = [self applyOnce:t Rule:whichRule];
162        treeChanged = !(t == u);
163        t = u;
164    }
165    return t;
166}
167
168- (id) downup:(ANTLRCommonTree *)t
169{
170    return [self downup:t XForm:NO];
171}
172
173- (id) pre:(ANTLRCommonTree *)t
174{
175    return [self applyOnce:t Rule:topdown_fptr];
176}
177
178- (id)post:(ANTLRCommonTree *)t
179{
180    return [self applyRepeatedly:t Rule:bottomup_ftpr];
181}
182
183#ifdef DONTUSENOMO
184public Object downup(Object t, boolean showTransformations) {
185    this.showTransformations = showTransformations;
186    TreeVisitor v = new TreeVisitor(new CommonTreeAdaptor());
187    TreeVisitorAction actions = new TreeVisitorAction() {
188        public Object pre(Object t)  { return applyOnce(t, topdown_fptr); }
189        public Object post(Object t) { return applyRepeatedly(t, bottomup_ftpr); }
190    };
191    t = v.visit(t, actions);
192    return t;
193}
194#endif
195
196- (id) downup:(ANTLRCommonTree *)t XForm:(BOOL)aShowTransformations
197{
198    showTransformations = aShowTransformations;
199    ANTLRTreeVisitor *v = [ANTLRTreeVisitor newANTLRTreeVisitor:[[originalAdaptor class] newTreeAdaptor]];
200    ANTLRTreeVisitorAction *actions = [ANTLRTreeVisitorAction newANTLRTreeVisitorAction];
201    {
202        //public Object pre(Object t)  { return applyOnce(t, topdown_fptr); }
203        [self pre:t];
204        //public Object post(Object t) { return applyRepeatedly(t, bottomup_ftpr); }
205        [self post:t];
206    };
207    t = [v visit:t Action:actions];
208    return t;
209}
210
211/** Override this if you need transformation tracing to go somewhere
212 *  other than stdout or if you're not using Tree-derived trees.
213 */
214- (void)reportTransformation:(ANTLRCommonTree *)oldTree Tree:(ANTLRCommonTree *)newTree
215{
216    //System.out.println(((Tree)oldTree).toStringTree()+" -> "+ ((Tree)newTree).toStringTree());
217}
218
219- (id)topdown_fptr
220{
221    return [self topdown];
222}
223
224- (id)bottomup_ftpr
225{
226    return [self bottomup];
227}
228
229// methods the downup strategy uses to do the up and down rules.
230// to override, just define tree grammar rule topdown and turn on
231// filter=true.
232- (id) topdown
233// @throws RecognitionException
234{
235    @throw [ANTLRRecognitionException newException:@"TopDown exception"];
236    return nil;
237}
238
239- (id) bottomup
240//@throws RecognitionException
241{
242    @throw [ANTLRRecognitionException newException:@"BottomUp exception"];
243    return nil;
244}
245
246@synthesize showTransformations;
247@synthesize originalTokenStream;
248@synthesize originalAdaptor;
249@synthesize rule;
250@end
251