Fixing an issue with output parameters that are of type IntPtr
[castle.git] / Experiments / Attic / Rook / Castle.Rook.Compiler / Services / Passes / DeclarationBinding.cs
blob7e8a5d7c558ff76ce08aae2566ccb1d76f872502
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.Rook.Compiler.Services.Passes
17 using System;
18 using System.Collections;
20 using Castle.Rook.Compiler.AST;
21 using Castle.Rook.Compiler.Visitors;
24 public class DeclarationBinding : BreadthFirstVisitor, ICompilerPass
26 private readonly IErrorReport errorReport;
27 private readonly IIdentifierNameService identifierService;
28 private int globalClassesCount = 1;
30 public DeclarationBinding(IErrorReport errorReport, IIdentifierNameService identifierService)
32 this.errorReport = errorReport;
33 this.identifierService = identifierService;
36 public void ExecutePass(CompilationUnit unit)
38 VisitNode(unit);
40 if (errorReport.HasErrors) return;
42 // Now we register the single var declaration as type members (fields)
43 // and ensure they are on the symbol table - sanity check
44 FieldsVisitor visitor = new FieldsVisitor();
45 visitor.VisitCompilationUnit(unit);
48 public override bool VisitSourceUnit(SourceUnit unit)
50 NamespaceGraph nsGraph = unit.SymbolTable.TypeGraphView.DefineNamespace("Castle.Rook.Code");
52 unit.SymbolTable.CurrentNamespace = nsGraph;
54 TypeDefinitionStatement typeDef = new TypeDefinitionStatement(unit.SymbolTable, AccessLevel.Private, String.Format("Global{0}", globalClassesCount++));
56 unit.SymbolTable.CurrentTypeDefinition = typeDef.SymbolTable.CurrentNamespace.AddTransientType( typeDef );
58 return base.VisitSourceUnit(unit);
61 public override bool VisitNamespace(NamespaceDeclaration ns)
63 if (!identifierService.IsValidNamespaceName(ns.Name))
65 errorReport.Error( "TODOFILENAME", ns.Position, "'{0}' is an invalid namespace name.", ns.Name );
67 return false;
70 ns.SymbolTable.CurrentNamespace = ns.SymbolTable.TypeGraphView.DefineNamespace(ns.Name);
72 return base.VisitNamespace(ns);
75 public override bool VisitTypeDefinitionStatement(TypeDefinitionStatement typeDef)
77 if (!identifierService.IsValidTypeName(typeDef.Name))
79 errorReport.Error( "TODOFILENAME", typeDef.Position, "'{0}' is an invalid type name.", typeDef.Name );
81 return false;
84 System.Diagnostics.Debug.Assert( typeDef.SymbolTable.CurrentNamespace != null );
86 typeDef.NamespaceName = typeDef.SymbolTable.CurrentNamespace.Name;
88 if (typeDef.SymbolTable.CurrentNamespace.GetType(typeDef.Name) != null)
90 errorReport.Error( "TODOFILENAME", typeDef.Position, "'{0}' has multiple definitions.", typeDef.Name );
92 return false;
95 typeDef.SymbolTable.CurrentTypeDefinition = typeDef.SymbolTable.CurrentNamespace.AddTransientType( typeDef );
97 return base.VisitTypeDefinitionStatement(typeDef);
100 public override bool VisitMethodDefinitionStatement(MethodDefinitionStatement methodDef)
102 if (!identifierService.IsValidMethodName(methodDef.Name))
104 errorReport.Error( "TODOFILENAME", methodDef.Position, "'{0}' is an invalid method name.", methodDef.Name );
106 return false;
109 TransientType owner = methodDef.SymbolTable.CurrentTypeDefinition;
111 System.Diagnostics.Debug.Assert( owner != null );
115 owner.AddMethod(methodDef);
117 catch(Exception ex)
119 errorReport.Error( "TODOFILENAME", methodDef.Position, ex.Message, methodDef.Name );
121 return false;
124 return base.VisitMethodDefinitionStatement(methodDef);
127 public override bool VisitRequireStatement(RequireStatement statement)
129 String name = statement.QualifiedIdentifier.Name;
131 if (!identifierService.IsValidNamespaceName( name ))
133 errorReport.Error( "TODOFILENAME", statement.Position, "You used a required statement for an invalid namespace '{0}'", name );
135 return false;
140 statement.SymbolTable.TypeGraphView.AddRequire( name );
142 catch(Exception ex)
144 errorReport.Error( "TODOFILENAME", statement.Position, ex.Message );
146 return false;
149 return base.VisitRequireStatement(statement);
152 public override bool VisitVariableReferenceExpression(VariableReferenceExpression varRef)
154 if (varRef.Identifier.Type == IdentifierType.Local)
156 String name = varRef.Identifier.Name;
157 ISymbolTable table = varRef.SymbolTable;
159 // It might be a variable or might be something else.
160 // First of all, let's see whether it is a var
162 bool isDefinedAsVar = false;
164 isDefinedAsVar = varRef.SymbolTable.IsDefined(name);
166 if (!isDefinedAsVar)
168 if (table.ScopeType == ScopeType.Block || table.ScopeType == ScopeType.Compound)
170 isDefinedAsVar = table.IsDefinedInParent(name);
174 if (!isDefinedAsVar)
176 // Ok, this might be two things now:
177 // an undeclared variable, a type ref or a member access... Let's check the type graph
179 TypeDefinition typeDef = varRef.SymbolTable.CurrentTypeDefinition;
181 System.Diagnostics.Debug.Assert( typeDef != null );
183 if (typeDef.HasInstanceMember(name) || typeDef.HasStaticMember(name))
185 // Wow! Finally
187 // TODO: Replace node by MemberAccess or method invocation, depending on the case
189 else
191 NamespaceGraph nsGraph = table.TypeGraphView.GetNamespace( name );
193 if (nsGraph == null)
195 TypeDefinition typedef = table.TypeGraphView.GetType( name );
197 if (typedef == null)
199 errorReport.Error( "TODOFILENAME", varRef.Position, "'{0}' undeclared identifier", name );
206 return base.VisitVariableReferenceExpression(varRef);
209 public override bool VisitMultipleVariableDeclarationStatement(MultipleVariableDeclarationStatement varDecl)
211 ProcessMultipleVariableDeclarationStatement(varDecl);
213 return base.VisitMultipleVariableDeclarationStatement(varDecl);
216 public override bool VisitAssignmentExpression(AssignmentExpression assignExp)
218 if (assignExp.Target.NodeType == NodeType.VariableRefExpression)
220 // Convert to declaration if not found on the scope
222 VariableReferenceExpression varRef = (VariableReferenceExpression) assignExp.Target;
224 ISymbolTable scope = varRef.SymbolTable;
226 System.Diagnostics.Debug.Assert( scope != null );
228 String name = varRef.Identifier.Name;
230 if (!scope.IsDefined(name)) // TODO: The rules are slighly more complicated than that.
232 errorReport.Disable();
234 SingleVariableDeclarationStatement varDecl = new SingleVariableDeclarationStatement(varRef.Identifier);
236 IStatement stmt = ASTUtils.GetParentStatement(varRef);
238 System.Diagnostics.Debug.Assert( stmt != null );
240 IStatementContainer stmts = stmt.Parent as IStatementContainer;
242 int index = stmts.Statements.IndexOf(stmt);
244 varDecl.InitExp = assignExp.Value;
246 // stmts.Statements.Insert(index, varDecl);
247 stmts.Statements.Replace(stmt, varDecl);
249 if (!ApplyDeclarationRules(varRef.Identifier, scope, varDecl, stmt))
251 stmts.Statements.Remove(varDecl);
254 errorReport.Enable();
258 return base.VisitAssignmentExpression(assignExp);
261 public override bool VisitParameterIdentifier(ParameterIdentifier parameterIdentifier)
263 ISymbolTable namescope = parameterIdentifier.Parent.SymbolTable;
265 System.Diagnostics.Debug.Assert( namescope != null );
266 System.Diagnostics.Debug.Assert( namescope.ScopeType == ScopeType.Method || namescope.ScopeType == ScopeType.Block );
268 if (!identifierService.IsValidFormatParameterName(parameterIdentifier.Name))
270 errorReport.Error( "TODOFILENAME", parameterIdentifier.Position, "'{0}' is an invalid parameter name.", parameterIdentifier.Name );
271 return false;
274 System.Diagnostics.Debug.Assert( !namescope.IsDefined(parameterIdentifier.Name) );
276 namescope.AddVariable( parameterIdentifier );
278 return base.VisitParameterIdentifier(parameterIdentifier);
281 public override bool VisitMemberAccessExpression(MemberAccessExpression accessExpression)
283 return base.VisitMemberAccessExpression(accessExpression);
286 private void ProcessMultipleVariableDeclarationStatement(MultipleVariableDeclarationStatement decl)
288 IList stmts = ConvertToSingleDeclarationStatements(decl);
290 ReplaceOriginalMultipleVariableDeclarationStatement(decl, stmts);
292 EnsureTypeDeclarationsBelongsToThisScope(decl, stmts);
295 private void ReplaceOriginalMultipleVariableDeclarationStatement(MultipleVariableDeclarationStatement decl, IList stmts)
297 int index;
299 // Replace the VariableDeclarationStatement node by a
300 // (possible) sequence of SingleVariableDeclarationStatement
302 IStatementContainer stmtContainer = decl.Parent as IStatementContainer;
304 index = stmtContainer.Statements.IndexOf(decl);
306 stmtContainer.Statements.RemoveAt(index);
308 foreach(SingleVariableDeclarationStatement svDecl in stmts)
310 stmtContainer.Statements.Insert( index++, svDecl );
314 private void EnsureTypeDeclarationsBelongsToThisScope(MultipleVariableDeclarationStatement varDecl, IList stmts)
316 ISymbolTable namescope = varDecl.Parent.SymbolTable;
318 System.Diagnostics.Debug.Assert( namescope != null );
320 foreach(SingleVariableDeclarationStatement typeDecl in stmts)
322 Identifier ident = typeDecl.Identifier;
324 // Most simple of cases: duplicated declaration
325 if (namescope.IsDefined(ident.Name))
327 errorReport.Error( "TODOFILENAME", typeDecl.Position, "Sorry but '{0}' is already defined.", ident.Name );
328 continue;
331 ApplyDeclarationRules(ident, namescope, typeDecl, varDecl);
335 private bool ApplyDeclarationRules(Identifier ident, ISymbolTable namescope, SingleVariableDeclarationStatement typeDecl, IStatement statem)
337 // Second simple case: a local var and we are on the right place to
338 // declare it
339 if (ident.Type == IdentifierType.Local &&
340 (namescope.ScopeType == ScopeType.Method ||
341 namescope.ScopeType == ScopeType.Compound ||
342 namescope.ScopeType == ScopeType.Block))
344 namescope.AddVariable(ident);
345 return true;
348 // More complex: a block or compound tries to redefine a variable
349 if (ident.Type == IdentifierType.Local &&
350 (namescope.ScopeType == ScopeType.Compound ||
351 namescope.ScopeType == ScopeType.Block))
353 if (namescope.Parent.IsDefined(ident.Name))
355 errorReport.Error( "TODOFILENAME", typeDecl.Position, "Sorry but '{0}' is already defined in a parent scope.", ident.Name );
356 return false;
360 // Local variables at class level?
361 // We will support that as a type initializer, but not now.
362 if (ident.Type == IdentifierType.Local && namescope.ScopeType == ScopeType.Type)
364 errorReport.Error( "TODOFILENAME", typeDecl.Position, "At type level, just instance or static fields are allowed (yet)" );
365 return false;
368 // Static or instance in a method/block/compound are moved
369 // to the parent class or source unit level
370 if (ident.Type == IdentifierType.InstanceField ||
371 ident.Type == IdentifierType.StaticField)
373 if (namescope.ScopeType == ScopeType.SourceUnit ||
374 namescope.ScopeType == ScopeType.Type)
376 namescope.AddVariable(ident);
378 else if (namescope.ScopeType == ScopeType.Method ||
379 namescope.ScopeType == ScopeType.Compound ||
380 namescope.ScopeType == ScopeType.Block)
382 IASTNode node = statem.Parent;
384 while(node != null &&
385 node.NodeType != NodeType.TypeDefinition &&
386 node.NodeType != NodeType.SourceUnit)
388 node = node.Parent;
391 if (node == null || node.SymbolTable == null)
393 errorReport.Error( "TODOFILENAME", typeDecl.Position,
394 "Compiler error: The instance of static declaration '{0}' could not be mapped to a parent type", ident.Name );
395 return false;
398 ISymbolTable parentScope = node.SymbolTable;
400 IStatementContainer typeStmtsContainer = node as IStatementContainer;
402 System.Diagnostics.Debug.Assert( parentScope != null );
403 System.Diagnostics.Debug.Assert( typeStmtsContainer != null );
405 if (parentScope.IsDefined(ident.Name))
407 errorReport.Error( "TODOFILENAME", typeDecl.Position,
408 "Sorry but '{0}' is already defined.", ident.Name );
409 return false;
411 else
413 parentScope.AddVariable(ident);
415 // We can replace the declaration on the method
416 // body with an assignment if and only if this type decl has
417 // an init expression, so CreateAssignmentFromTypeDecl can return null
418 AssignmentExpression assignExp = CreateAssignmentFromTypeDecl(typeDecl);
419 ExpressionStatement assignExpStmt = new ExpressionStatement(assignExp);
421 typeDecl.ConvertInitExpressionToDependency();
423 // Replace the declaration with an assignment
424 (statem.Parent as IStatementContainer).Statements.Replace(typeDecl, assignExpStmt);
426 // Add the member/field declaration to the parent
427 typeStmtsContainer.Statements.Add( typeDecl );
429 // TODO: Link assignment expression and typeDecl to help
430 // find out the type of the field later
432 return true;
437 return false;
440 private IList ConvertToSingleDeclarationStatements(MultipleVariableDeclarationStatement varDecl)
442 IList newStmts = new ArrayList();
444 int index = 0;
445 ExpressionCollection initExps = varDecl.InitExpressions;
447 foreach(Identifier ident in varDecl.Identifiers)
449 // Here we are converting expression from
450 // x:int, y:long = 1, 2L
451 // to an AST representation equivalent to
452 // x:int = 1; y:long = 2L
454 SingleVariableDeclarationStatement svStmt = new SingleVariableDeclarationStatement(ident);
456 if (index < initExps.Count)
458 svStmt.InitExp = initExps[index];
459 EnsureNoPostFixStatement(svStmt.InitExp);
462 index++;
464 newStmts.Add(svStmt);
467 // We don't need them anymore
468 initExps.Clear();
470 return newStmts;
473 private void EnsureNoPostFixStatement(IExpression initExpression)
475 if (initExpression.PostFixStatement != null)
477 errorReport.Error( "TODOFILENAME", initExpression.Position,
478 "Sorry but a variable initializer can not be conditional or " +
479 "has a while/until statement attached.");
483 private AssignmentExpression CreateAssignmentFromTypeDecl(SingleVariableDeclarationStatement decl)
485 if (decl.InitExp == null) return null;
487 return new AssignmentExpression( new VariableReferenceExpression(decl.Identifier), decl.InitExp );
491 public class FieldsVisitor : BreadthFirstVisitor
493 public override bool VisitSingleVariableDeclarationStatement(SingleVariableDeclarationStatement varDecl)
495 PerformSanityCheck(varDecl);
497 CreateField(varDecl);
499 return base.VisitSingleVariableDeclarationStatement(varDecl);
502 private void CreateField(SingleVariableDeclarationStatement decl)
504 if (decl.Identifier.Type == IdentifierType.Local) return;
506 TransientType type = decl.SymbolTable.CurrentTypeDefinition;
508 System.Diagnostics.Debug.Assert( type != null );
510 type.AddField( decl );
513 private void PerformSanityCheck(SingleVariableDeclarationStatement decl)
515 // TODO: Ensure that the symbol table are capable of holding a var declaration
517 if (!decl.SymbolTable.IsDefined( decl.Identifier.Name ))
519 throw new CompilerException("Found single var declaration node that was not on the symbol table: " + decl.Identifier.Name);