You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
7.4 KiB
250 lines
7.4 KiB
// <file> |
|
// <copyright see="prj:///doc/copyright.txt"/> |
|
// <license see="prj:///doc/license.txt"/> |
|
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/> |
|
// <version>$Revision$</version> |
|
// </file> |
|
|
|
using Microsoft.Scripting; |
|
using System; |
|
using System.Collections.Generic; |
|
using System.IO; |
|
|
|
using ICSharpCode.SharpDevelop.Dom; |
|
using IronPython.Compiler; |
|
using IronPython.Compiler.Ast; |
|
using IronPython.Runtime; |
|
|
|
namespace ICSharpCode.PythonBinding |
|
{ |
|
/// <summary> |
|
/// Walks the python parse tree. |
|
/// </summary> |
|
public class PythonAstWalker : PythonWalker |
|
{ |
|
DefaultCompilationUnit compilationUnit; |
|
IClass currentClass; |
|
IClass globalClass; |
|
|
|
/// <summary> |
|
/// All classes in a file take the namespace of the filename. |
|
/// </summary> |
|
public PythonAstWalker(IProjectContent projectContent, string fileName) |
|
{ |
|
CreateCompilationUnit(projectContent, fileName); |
|
} |
|
|
|
void CreateCompilationUnit(IProjectContent projectContent, string fileName) |
|
{ |
|
compilationUnit = new DefaultCompilationUnit(projectContent); |
|
compilationUnit.FileName = fileName; |
|
|
|
CreateUsingScopeForCompilationUnit(fileName); |
|
} |
|
|
|
void CreateUsingScopeForCompilationUnit(string fileName) |
|
{ |
|
DefaultUsingScope usingScope = new DefaultUsingScope(); |
|
usingScope.NamespaceName = Path.GetFileNameWithoutExtension(fileName); |
|
usingScope.Parent = new DefaultUsingScope(); |
|
compilationUnit.UsingScope = usingScope; |
|
} |
|
|
|
/// <summary> |
|
/// Returns the compilation unit created after the Walk method |
|
/// has been called. |
|
/// </summary> |
|
public ICompilationUnit CompilationUnit { |
|
get { return compilationUnit; } |
|
} |
|
|
|
/// <summary> |
|
/// Walks the python statement returned from the parser. |
|
/// </summary> |
|
public void Walk(Statement statement) |
|
{ |
|
statement.Walk(this); |
|
} |
|
|
|
/// <summary> |
|
/// Walks a class definition. |
|
/// </summary> |
|
public override bool Walk(ClassDefinition node) |
|
{ |
|
DefaultClass c = new DefaultClass(compilationUnit, GetFullyQualifiedClassName(node)); |
|
c.Region = GetRegion(node); |
|
c.BodyRegion = GetBodyRegion(node.Body, node.Header); |
|
AddBaseTypes(c, node.Bases); |
|
|
|
// Save the class. |
|
compilationUnit.Classes.Add(c); |
|
|
|
// Walk through all the class items. |
|
currentClass = c; |
|
node.Body.Walk(this); |
|
currentClass = null; |
|
|
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Walks a function definition. |
|
/// </summary> |
|
public override bool Walk(FunctionDefinition node) |
|
{ |
|
if (node.Body == null) { |
|
return false; |
|
} |
|
|
|
bool ignoreFirstMethodParameter = true; |
|
IClass c = currentClass; |
|
if (currentClass == null) { |
|
// Walking a global method. |
|
CreateGlobalClass(); |
|
c = globalClass; |
|
ignoreFirstMethodParameter = false; |
|
} |
|
|
|
// Create method. |
|
string methodName = node.Name; |
|
DomRegion bodyRegion = GetBodyRegion(node.Body, node.Header); |
|
DomRegion region = GetMethodRegion(node); |
|
|
|
DefaultMethod method; |
|
if (methodName == "__init__") { |
|
method = new Constructor(ModifierEnum.Public, region, bodyRegion, c); |
|
} else { |
|
method = new DefaultMethod(methodName, new DefaultReturnType(c), ModifierEnum.Public, region, bodyRegion, c); |
|
} |
|
foreach (IParameter parameter in ConvertParameters(node.Parameters, ignoreFirstMethodParameter)) { |
|
method.Parameters.Add(parameter); |
|
} |
|
c.Methods.Add(method); |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Walks an import statement and adds it to the compilation unit's |
|
/// Usings. |
|
/// </summary> |
|
public override bool Walk(ImportStatement node) |
|
{ |
|
PythonImport import = new PythonImport(compilationUnit.ProjectContent, node); |
|
compilationUnit.UsingScope.Usings.Add(import); |
|
return false; |
|
} |
|
|
|
public override bool Walk(FromImportStatement node) |
|
{ |
|
PythonFromImport import = new PythonFromImport(compilationUnit.ProjectContent, node); |
|
compilationUnit.UsingScope.Usings.Add(import); |
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the body region for a class or a method. |
|
/// </summary> |
|
/// <remarks> |
|
/// Note that SharpDevelop line numbers are zero based but the |
|
/// DomRegion values are one based. IronPython columns and lines are one based. |
|
/// </remarks> |
|
/// <param name="body">The body statement.</param> |
|
/// <param name="header">The location of the header. This gives the end location for the |
|
/// method or class definition up to the colon.</param> |
|
DomRegion GetBodyRegion(Statement body, SourceLocation header) |
|
{ |
|
int columnAfterColonCharacter = header.Column + 1; |
|
return new DomRegion(header.Line, header.Column + 1, body.End.Line, body.End.Column); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the region of the scope statement (typically a ClassDefinition). |
|
/// </summary> |
|
/// <remarks> |
|
/// A class region includes the body. |
|
/// </remarks> |
|
DomRegion GetRegion(ScopeStatement statement) |
|
{ |
|
return new DomRegion(statement.Start.Line, statement.Start.Column, statement.End.Line, statement.End.Column); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the region of a method. This does not include the body. |
|
/// </summary> |
|
DomRegion GetMethodRegion(FunctionDefinition node) |
|
{ |
|
return new DomRegion(node.Start.Line, node.Start.Column, node.Header.Line, node.Header.Column + 1); |
|
} |
|
|
|
/// <summary> |
|
/// Looks for any base types for the class defined in the |
|
/// list of expressions and adds them to the class. |
|
/// </summary> |
|
void AddBaseTypes(IClass c, IList<Expression> baseTypes) |
|
{ |
|
foreach (Expression expression in baseTypes) { |
|
NameExpression nameExpression = expression as NameExpression; |
|
MemberExpression memberExpression = expression as MemberExpression; |
|
if (nameExpression != null) { |
|
AddBaseType(c, nameExpression.Name); |
|
} else if (memberExpression != null) { |
|
AddBaseType(c, PythonControlFieldExpression.GetMemberName(memberExpression)); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Adds the named base type to the class. |
|
/// </summary> |
|
void AddBaseType(IClass c, string name) |
|
{ |
|
c.BaseTypes.Add(CreateSearchClassReturnType(c, name)); |
|
} |
|
|
|
SearchClassReturnType CreateSearchClassReturnType(IClass c, string name) |
|
{ |
|
return new SearchClassReturnType(c.ProjectContent, c, 0, 0, name, 0); |
|
} |
|
|
|
/// <summary> |
|
/// Converts from Python AST expressions to parameters. |
|
/// </summary> |
|
/// <remarks>If the parameters belong to a class method then the first |
|
/// "self" parameter can be ignored.</remarks> |
|
IParameter[] ConvertParameters(IList<Parameter> parameters, bool ignoreFirstParameter) |
|
{ |
|
List<IParameter> convertedParameters = new List<IParameter>(); |
|
|
|
int startingIndex = 0; |
|
if (ignoreFirstParameter) { |
|
startingIndex = 1; |
|
} |
|
|
|
for (int i = startingIndex; i < parameters.Count; ++i) { |
|
DefaultParameter parameter = new DefaultParameter(parameters[i].Name, null, new DomRegion()); |
|
convertedParameters.Add(parameter); |
|
} |
|
return convertedParameters.ToArray(); |
|
} |
|
|
|
|
|
/// <summary> |
|
/// Adds the namespace to the class name taken from the class definition. |
|
/// </summary> |
|
string GetFullyQualifiedClassName(ClassDefinition classDef) |
|
{ |
|
return String.Concat(compilationUnit.UsingScope.NamespaceName, ".", classDef.Name); |
|
} |
|
|
|
/// <summary> |
|
/// Creates the dummy class that is used to hold global methods. |
|
/// </summary> |
|
void CreateGlobalClass() |
|
{ |
|
if (globalClass == null) { |
|
globalClass = new DefaultClass(compilationUnit, compilationUnit.UsingScope.NamespaceName); |
|
compilationUnit.Classes.Add(globalClass); |
|
} |
|
} |
|
} |
|
}
|
|
|