using System.Collections.Generic; using System.IO; using System.Text; using CppNet; namespace TwoMGFX { public static class Preprocessor { public static string Preprocess( string effectCode, string filePath, IDictionary defines, List dependencies, IEffectCompilerOutput output) { var fullPath = Path.GetFullPath(filePath); var pp = new CppNet.Preprocessor(); pp.EmitExtraLineInfo = false; pp.addFeature(Feature.LINEMARKERS); pp.setListener(new MGErrorListener(output)); pp.setFileSystem(new MGFileSystem(dependencies)); pp.setQuoteIncludePath(new List { Path.GetDirectoryName(fullPath) }); foreach (var define in defines) pp.addMacro(define.Key, define.Value); effectCode = effectCode.Replace("#line", "//--WORKAROUND#line"); pp.addInput(new MGStringLexerSource(effectCode, true, fullPath)); var result = new StringBuilder(); var endOfStream = false; while (!endOfStream) { var token = pp.token(); switch (token.getType()) { case CppNet.Token.EOF: endOfStream = true; break; case CppNet.Token.CPPCOMMENT: if (token.getText().StartsWith("//--WORKAROUND#line")) { result.Append(token.getText().Replace("//--WORKAROUND#line", "#line")); } break; case CppNet.Token.CCOMMENT: { var tokenText = token.getText(); if (tokenText != null) { // Need to preserve line breaks so that line numbers are correct. foreach (var c in tokenText) if (c == '\n') result.Append(c); } break; } default: { var tokenText = token.getText(); if (tokenText != null) result.Append(tokenText); break; } } } return result.ToString(); } private class MGFileSystem : VirtualFileSystem { private readonly List _dependencies; public MGFileSystem(List dependencies) { _dependencies = dependencies; } public VirtualFile getFile(string path) { return new MGFile(path, _dependencies); } public VirtualFile getFile(string dir, string name) { return new MGFile(Path.Combine(dir, name), _dependencies); } } private class MGFile : VirtualFile { private readonly List _dependencies; private readonly string _path; public MGFile(string path, List dependencies) { _dependencies = dependencies; _path = Path.GetFullPath(path); } public bool isFile() { return File.Exists(_path) && !File.GetAttributes(_path).HasFlag(FileAttributes.Directory); } public string getPath() { return _path; } public string getName() { return Path.GetFileName(_path); } public VirtualFile getParentFile() { return new MGFile(Path.GetDirectoryName(_path), _dependencies); } public VirtualFile getChildFile(string name) { return new MGFile(Path.Combine(_path, name), _dependencies); } public Source getSource() { if (!_dependencies.Contains(_path)) _dependencies.Add(_path); return new MGStringLexerSource(AppendNewlineIfNonePresent(File.ReadAllText(_path)), true, _path); } private static string AppendNewlineIfNonePresent(string text) { if (!text.EndsWith("\n")) return text + "\n"; return text; } } private class MGStringLexerSource : StringLexerSource { public string Path { get; private set; } public MGStringLexerSource(string str, bool ppvalid, string fileName) : base(str.Replace("\r\n", "\n"), ppvalid, fileName) { Path = fileName; } } private class MGErrorListener : PreprocessorListener { private readonly IEffectCompilerOutput _output; public MGErrorListener(IEffectCompilerOutput output) { _output = output; } public void handleWarning(Source source, int line, int column, string msg) { _output.WriteWarning(GetPath(source), line, column, msg); } public void handleError(Source source, int line, int column, string msg) { _output.WriteError(GetPath(source), line, column, msg); } private string GetPath(Source source) { return ((MGStringLexerSource) source).Path; } public void handleSourceChange(Source source, string ev) { } } } }