View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2001-2004 Open World Ltd
4   // Copyright (C) 2005 Kevin Rogers
5   //
6   // This file is part of MillScript.
7   //
8   // MillScript is free software; you can redistribute it and/or modify it under
9   // the terms of the GNU General Public License as published by the Free
10  // Software Foundation; either version 2 of the License, or (at your option)
11  // any later version.
12  //
13  // MillScript is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  // more details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA  02111-1307  USA
21  ////////////////////////////////////////////////////////////////////////////////
22  package org.millscript.millscript.expr;
23  
24  import org.millscript.commons.util.IList;
25  import org.millscript.millscript.action.PushAction;
26  import org.millscript.millscript.action.PushAlienAction;
27  import org.millscript.millscript.action.PushGlobalAction;
28  import org.millscript.millscript.action.PushLocalAction;
29  import org.millscript.millscript.alert.Alerts;
30  import org.millscript.millscript.vm.CompilerState;
31  import org.millscript.millscript.vm.Ref;
32  
33  import java.util.Stack;
34  
35  /**
36   * This class implements a name expression. This expression can only return one
37   * result, which will be the value associated with the name.
38   *
39   * @see PushAlienAction
40   * @see PushLocalAction
41   * @see PushGlobalAction
42   */
43  public final class NameExpr extends Expr< PushAction > implements OneResult {
44  
45      /**
46       * The ident for this name. The ident is the result of renaming/compiling,
47       * and idents are shared across name expressions which identify the same
48       * name.
49       *
50       * @see #rename
51       */
52      private Ident ident;
53  
54      /**
55       * The kind of variable this name represents.
56       *
57       * <dl>
58       * <dt>U</dt>
59       * <dd>unclassified</dd>
60       * <dt>G</dt>
61       * <dd>global</dd>
62       * <dt>L</dt>
63       * <dd>local</dd>
64       * <dt>N</dt>
65       * <dd>nonlocal (i.e. "type 3" local)</dd>
66       * </dl>
67       */
68      private int kind = 'U';
69  
70      /**
71       * The package nickname associated with this name.
72       */
73      private String nickname = null;
74  
75      /**
76       * The symbol associated with this name.
77       */
78      private String sym;
79  
80      /**
81       * Creates a new name expression for the specified symbol.
82       *
83       * @param   s   the symbol for this name expression
84       */
85      public NameExpr( final String s ) {
86          sym = s.intern();
87      }
88  
89      /**
90       * Annotate any functions in scope, to the relevant depth, with this name
91       * expression.
92       *
93       * <p>
94       * TODO - This comment is probably wrong
95       * </p>
96       *
97       * @param   state   the compiler state whose scope to annotate
98       * @param   depth   the depth of scope to annotate
99       */
100     private void annotate( final CompilerState state, final int depth ) {
101         Stack< Expr > scopes = state.getScopes();
102         for ( int j = scopes.size() - 1; j > depth; j-- ) {
103             Expr e = scopes.elementAt( j );
104             if ( e instanceof LambdaExpr ) {
105                 ((LambdaExpr)e).addOuter( this );
106             }
107         }
108     }
109 
110     /**
111      * {@inheritDoc}
112      *
113      * @return  a {@link PushAlienAction}, {@link PushLocalAction} or a
114      *          {@link PushGlobalAction} for this expression, depending on the
115      *          type of variable this name represents
116      */
117     @Override
118     public PushAction compileIt() {
119         if ( ident instanceof LocalIdent ) {
120             if ( ident.isAlien() ) {
121                 return new PushAlienAction( ident );
122             } else {
123                 return new PushLocalAction( ident );
124             }
125         } else if ( ident instanceof GlobalIdent ) {
126             return new PushGlobalAction( ident );
127         } else {
128             throw(
129                 Alerts.fault(
130                     "Unknown ident while compiling NameExpr"
131                 ).
132                 culprit( "name", sym ).
133                 culprit( "ident", ident ).
134                 mishap()
135             );
136         }
137     }
138 
139     /**
140      * Declares this name expression to be a global identifier in the specified
141      * compiler state, with the specified constant state.
142      *
143      * @param   state   the compiler state in which to declare this name a
144      *                  global identifier
145      * @param   isConst a boolean indicating if this new global identifier is a
146      *                  constant
147      */
148     public void declareGlobal( final CompilerState state, final boolean isConst ) {
149         ident = state.declareGlobal( sym );
150         ident.setIsConst( isConst );
151     }
152 
153     /**
154      * Declares this name expression to be a local identifier, with the
155      * specified constant state.
156      *
157      * @param   isConst a boolean indicating if this new local identifier is a
158      *                  constant
159      */
160     public void declareLocal( final boolean isConst ) {
161         setIdent( new LocalIdent( sym, new Ref() ).setIsConst( isConst ) );
162     }
163 
164     /**
165      * Returns the ident for this name expression.
166      *
167      * @return  the {@link Ident} for this name
168      */
169     public Ident getIdent() {
170         if ( ident == null ) {
171             throw(
172                 Alerts.compile(
173                     "Name with unassigned ident",
174                     "Sorry, context not available"
175                 ).culprit( "symbol", sym ).mishap()
176             );
177         }
178         return ident;
179     }
180 
181     /**
182      * Returns the symbol for this name expression.
183      *
184      * @return  a string containing the symbol for this name expression
185      */
186     public String getName() {
187         return sym;
188     }
189 
190     /**
191      * Returns the package nickname for this name expression.
192      *
193      * @return  a string containing the package nickname for this name
194      *          expression
195      */
196     public String getNickname() {
197         return nickname;
198     }
199 
200     /**
201      * @see org.millscript.millscript.expr.Expr#resolve(org.millscript.millscript.vm.CompilerState)
202      *
203      * <p>
204      * A name expression has a complicated renaming process. This is responsible
205      * for ensuring idents are shared properly, so that variables of the same
206      * name really do refer to the same variable.
207      * </p>
208      */
209     @Override
210     public void resolve( final CompilerState state ) {
211         // Get the current scope
212         Stack< Expr > scopes = state.getScopes();
213         // Find the size of the current scope
214         int n = scopes.size();
215         // Counts the number of lambda expressions in scope, i.e this will
216         // contain the number of nested functions
217         int countLambdas = 0;
218         // Loop through the current scope from the "inside" out, i.e. start at
219         // the innermost scope.
220         for ( int i = n - 1; i >= 0; i-- ) {
221             Expr e = scopes.elementAt( i );
222             if ( e instanceof LambdaExpr ) {
223                 final IList< NameExpr > args = ((LambdaExpr)e).getArgs();
224                 for ( int argPos = 1; argPos <= args.size(); argPos++ ) {
225                     NameExpr nam = args.get( argPos );
226                     if ( shareIdent( nam, countLambdas ) ) {
227                         annotate( state, i );
228                         return;
229                     }
230                 }
231                 NameExpr nme = ((LambdaExpr)e).getRest();
232                 if ( nme != null ) {
233                     if ( shareIdent( nme, countLambdas ) ) {
234                         annotate( state, i );
235                         return;
236                     }
237                 }
238                 countLambdas += 1;
239             } else if ( e instanceof NameExpr ) {
240                 NameExpr nam = (NameExpr)e;
241                 if ( shareIdent( nam, countLambdas ) ) {
242                     annotate( state, i );
243                     return;
244                 }
245             } else if ( e instanceof Block ) {
246                 //    skip
247             } else {
248                 throw(
249                     Alerts.fault( "Invalid data structure in scope chain" ).mishap()
250                 );
251             }
252         }
253         setIdent( state.findIdent( nickname, sym ) );
254     }
255 
256     /**
257      * Sets this name expressions ident to that specified.
258      *
259      * @param   id  the ident for this name expression
260      */
261     public void setIdent( final Ident id ) {
262         ident = id;
263     }
264 
265     /**
266      * Sets the package nickname for this name expression.
267      *
268      * @param   s   the package nickname for this name expression
269      */
270     public void setNickname( final String s ) {
271         nickname = s;
272     }
273 
274     /**
275      * Conditionally shares the specified name expressions ident with this
276      * instances ident. Idents are only shared if both name expressions have the
277      * same package nickname and symbol. The specified name expressions ident is
278      * set as this instances ident.
279      *
280      * @param   n   the name expression to conditionally share idents with
281      * @param   countLambdas    an integer which counts the number of nested
282      *              functions in the current compiler scope.
283      * @return  <code>true</code> if the specified name expression now shares
284      *          idents with this instance, otherwise <code>false</code>
285      */
286     boolean shareIdent( final NameExpr n, final int countLambdas ) {
287         if ( nickname == n.getNickname() && sym == n.getName() ) {
288             LocalIdent id = (LocalIdent)n.getIdent();
289             if ( countLambdas > 0 ) {
290                 System.out.println( "Found non-local reference to " + sym );
291                 id.markAsOuter();
292             }
293             this.setIdent( id );
294             return true;
295         } else {
296             return false;
297         }
298     }
299 
300     /**
301      * @see org.millscript.millscript.expr.Expr#show(int)
302      */
303     @Override
304     public void show( final int n ) {
305         showClass( n );
306         indent( n + 1, sym );
307     }
308 
309 }