1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
212 Stack< Expr > scopes = state.getScopes();
213
214 int n = scopes.size();
215
216
217 int countLambdas = 0;
218
219
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
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 }