Show Parser.cs syntax highlighted
/* **********************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This source code is subject to terms and conditions of the Shared Source License
* for IronPython. A copy of the license can be found in the License.html file
* at the root of this distribution. If you can not locate the Shared Source License
* for IronPython, please send an email to ironpy@microsoft.com.
* By using this source code in any fashion, you are agreeing to be bound by
* the terms of the Shared Source License for IronPython.
*
* You must not remove this notice, or any other, from this software.
*
* **********************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler;
using IronPython.Compiler;
using IronPython.Compiler.Ast;
using IronPython.Runtime;
using System.Diagnostics;
namespace IronPython.CodeDom {
class PythonParser : CodeParser, IDisposable {
private List<string> references = new List<string>();
private SystemState state = new SystemState();
public PythonParser(List<string> assemblyReferences) {
if (assemblyReferences.Count == 0) {
references.Add("System.Windows.Forms");
references.Add("System");
references.Add("System.Data");
references.Add("System.Drawing");
references.Add("System.Xml");
references.Add("mscorlib");
} else {
references.AddRange(assemblyReferences);
}
}
/// <summary>
/// Parse an entire file - ideal path as we'll respect
/// PEP-263 encodings and the such...
/// </summary>
public CodeCompileUnit ParseFile(string filename) {
return Parse(Parser.FromFile(state, new CompilerContext(filename)), filename);
}
/// <summary>
/// Parse an arbitrary stream
/// </summary>
public override CodeCompileUnit Parse(System.IO.TextReader codeStream) {
return ParseMergeable(codeStream, null);
}
public CodeCompileUnit ParseMergeable(string text, string filename, IMergeDestination destination){
CodeCompileUnit tree = Parse(Parser.FromString(state, new CompilerContext(), text), filename);
CodeMerger.CacheCode(tree, destination);
return tree;
}
public CodeCompileUnit ParseMergeable(System.IO.TextReader codeStream, IMergeDestination destination) {
// get a better filename if we can
string name = "<unknown>";
StreamReader sw = codeStream as StreamReader;
if (sw != null) {
FileStream fs = sw.BaseStream as FileStream;
if (fs != null) name = fs.Name;
}
//!!! it'd be nice to have Parser.FromStream to get proper decodings here
string codeText = codeStream.ReadToEnd();
CodeCompileUnit tree = Parse(Parser.FromString(state, new CompilerContext(), codeText), name);
if (destination != null) CodeMerger.CacheCode(tree, destination);
else CodeMerger.CacheCode(tree, codeText);
return tree;
}
/// <summary>
/// Parses an arbitrary string of Python code
/// </summary>
public CodeCompileUnit Parse(string text) {
return Parse(Parser.FromString(state, new CompilerContext(), text), "<unknown>");
}
/// <summary>
/// Private helper function for all parsing. Takes in the IronPython Parser
/// object and a filename that's used for error reports
/// </summary>
/// <param name="p"></param>
private CodeCompileUnit Parse(Parser p, string filename) {
Statement s = p.ParseFileInput();
CodeCompileUnit res = new CodeCompileUnit();
CodeNamespace defaultNamespace = new CodeNamespace();
defaultNamespace.UserData["Line"] = 1;
defaultNamespace.UserData["Column"] = 1;
defaultNamespace.UserData["EndLine"] = 1;
defaultNamespace.UserData["EndColumn"] = 1;
//!!! enable AD usage when we're strong named.
//AppDomainSetup ads = new AppDomainSetup();
//ads.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
//AppDomain ad = AppDomain.CreateDomain("ParserReferenceDomain",null,ads);
try {
RemoteReferences rc = new RemoteReferences();
/*(RemoteReferences)ad.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName,
"IronPython.CodeDom.RemoteReferences");*/
rc.Initialize(references);
CodeWalker cw = new CodeWalker(filename, rc);
s.Walk(cw);
CodeObject co = cw.LastObject;
CodeObjectSuite cos = co as CodeObjectSuite;
if (cos == null) cos = new CodeObjectSuite(new CodeObject[] { co });
// walk the top-level and see if we need to create a fake top-type
// or if we can just stick everything directly into the namespace.
CodeTypeDeclaration topType = null;
for (int i = 0; i < cos.Count; i++) {
topType = CheckTopLevel(cos[i], res, defaultNamespace, topType);
}
// if no namespaces we're added then everything's in our default namespace.
if (res.Namespaces.Count == 0 || defaultNamespace.Types.Count > 0) {
res.Namespaces.Add(defaultNamespace);
}
UpdateTopType(topType, defaultNamespace);
} finally {
//AppDomain.Unload(ad);
}
return res;
}
private static CodeTypeDeclaration CheckTopLevel(CodeObject cur, CodeCompileUnit res, CodeNamespace defaultNamespace, CodeTypeDeclaration topType) {
if (cur is CodeNamespaceImport) {
defaultNamespace.Imports.Add(cur as CodeNamespaceImport);
} else if (cur is CodeTypeDeclaration) {
CodeTypeDeclaration ctd = cur as CodeTypeDeclaration;
bool fRealClass = false;
if (ctd.BaseTypes.Count == 0) {
foreach (CodeTypeMember mem in ctd.Members) {
if (mem is CodeTypeDeclaration)
continue;
fRealClass = true;
break;
}
} else {
fRealClass = true;
}
if (fRealClass) {
defaultNamespace.Types.Add(ctd);
} else {
// class w/ nested types, transfer the
// type's content to a namespace.
TypeToNamespace(res, defaultNamespace, ctd);
}
} else if (cur is CodeNamespace) {
res.Namespaces.Add(cur as CodeNamespace);
} else if (cur is CodeExpressionStatement) {
ValidateCodeExpression(cur as CodeExpressionStatement);
} else {
if (topType == null) topType = new CodeTypeDeclaration("__top__");
topType.Members.Add((CodeTypeMember)cur);
}
return topType;
}
private static void TypeToNamespace(CodeCompileUnit res, CodeNamespace defaultNamespace, CodeTypeDeclaration ctd) {
CodeNamespace nsRes = new CodeNamespace(ctd.Name);
res.Namespaces.Add(nsRes);
foreach (CodeTypeMember mem in ctd.Members) {
nsRes.Types.Add((CodeTypeDeclaration)mem);
}
CopyLineInfo(ctd, nsRes);
foreach (CodeNamespaceImport cn in defaultNamespace.Imports) {
nsRes.Imports.Add(cn);
}
}
private static void CopyLineInfo(CodeTypeDeclaration ctd, CodeNamespace nsRes) {
nsRes.UserData["PreImport"] = true;
nsRes.UserData["IPCreated"] = ctd.UserData["IPCreated"];
nsRes.UserData["EndLine"] = ctd.UserData["EndLine"];
nsRes.UserData["EndColumn"] = ctd.UserData["EndColumn"];
nsRes.UserData["Column"] = ctd.UserData["Column"];
nsRes.UserData["Line"] = ctd.UserData["Line"];
}
private static void UpdateTopType(CodeTypeDeclaration topType, CodeNamespace defaultNamespace) {
if (topType != null) {
// we had a non-type at the top-level, we need to transfer
// all our types from the default name space to the top-type
for (int i = 0; i < defaultNamespace.Types.Count; i++) {
topType.Members.Insert(i, defaultNamespace.Types[i]);
}
defaultNamespace.Types.Clear();
topType.UserData["IsTopType"] = true;
}
}
private static void ValidateCodeExpression(CodeExpressionStatement ces) {
CodeMethodInvokeExpression invoke = ces.Expression as CodeMethodInvokeExpression;
if (invoke != null) {
CodeFieldReferenceExpression target = invoke.Method.TargetObject as CodeFieldReferenceExpression;
if (target != null) {
if (target.FieldName != "RealEntryPoint") throw new NotImplementedException("arbitrary calls at the top-level");
} else if (invoke.Method.MethodName != "RealEntryPoint") throw new NotImplementedException("arbitrary calls at the top-level");
// otherwise we want to ignore the statement.
} else {
throw new NotImplementedException("arbitrary expressions at the top-level");
}
}
~PythonParser() {
Dispose(true);
}
#region IDisposable Members
public void Dispose() {
Dispose(false);
}
#endregion
private void Dispose(bool finalizing) {
// if we're finalizing we shouldn't access other managed objects, as
// their finalizers may have already run
if (!finalizing) {
state.Dispose();
}
}
}
class CodeObjectSuite : CodeObject {
List<CodeObject> objects = new List<CodeObject>();
public CodeObjectSuite() {
}
public CodeObjectSuite(CodeObject[] objs) {
objects.AddRange(objs);
}
public int Count {
get {
return objects.Count;
}
}
public CodeObject this[int index] {
get {
return objects[index];
}
set {
objects[index] = value;
}
}
public void Add(CodeObject co) {
objects.Add(co);
}
public void RemoveAt(int index) {
objects.RemoveAt(index);
}
}
}
See more files for this project here