Skip to content
This repository has been archived by the owner on Jan 3, 2023. It is now read-only.

Commit

Permalink
Initial working implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
stever committed Sep 29, 2018
1 parent ca22a22 commit 59e8701
Show file tree
Hide file tree
Showing 15 changed files with 5,396 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,6 @@ ASALocalRun/

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# ANTLR output
gen/
183 changes: 182 additions & 1 deletion src/Lua Loader/AddIn.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,198 @@
using ExcelDna.Integration;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using ExcelDna.Integration;
using log4net;
using MoonSharp.Interpreter;

namespace LuaForExcel.LuaLoader
{
public class AddIn : IExcelAddIn
{
private static readonly ILog Log = LogManager.
GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

private static readonly NetOffice.ExcelApi.Application Excel
= new NetOffice.ExcelApi.Application(null, ExcelDnaUtil.Application);

private static readonly Dictionary<string, Script> Scripts;

static AddIn()
{
Log.Debug("static AddIn");
Scripts = new Dictionary<string, Script>(StringComparer.OrdinalIgnoreCase);
}

public void AutoOpen()
{
Log.Debug("AutoOpen");

Scripts.Clear();

// Registration takes a list of Delegates
var delegates = new List<Delegate>();
var functionAttributes = new List<object>();
var argAttributesLists = new List<List<object>>();

try
{
var luaScripts = GetLuaScripts();
foreach (var obj in luaScripts)
{
var scriptName = obj.Key;
var luaScript = obj.Value;

// http://www.moonsharp.org/sandbox.html
var script = new Script(CoreModules.Preset_HardSandbox) {Options =
{
DebugPrint = s => Log.InfoFormat("{0}: {1}", scriptName ?? "Lua print", s),
CheckThreadAccess = false
}};

script.DoString(luaScript);

foreach (var def in LuaFunctions.GetFunctionDefinitions(luaScript))
{
if (Scripts.ContainsKey(def.Name))
{
Log.WarnFormat("Ignoring redefined Lua function: {0}", def.Name);
continue;
}

if (def.Args.Count > 16)
{
Log.ErrorFormat("Functions with more than 16 arguments cannot be registered with Excel-DNA: {0} ({1})", def.Name, def.Args.Count);
continue;
}

// Add the function script to be executed.
Log.InfoFormat("Function: {0}", def.Name);
Scripts.Add(def.Name, script);

// Add a delegate for the function call.
delegates.Add(GetDelegate(def.Name, def.Args.Count));

// Add the function attribute.
// https://github.com/Excel-DNA/ExcelDna/wiki/ExcelFunction-and-other-attributes
functionAttributes.Add(new ExcelFunctionAttribute
{
Name = def.Name,
Description = "",
IsVolatile = true,
IsHidden = false,
IsThreadSafe = false,
IsClusterSafe = false,
IsExceptionSafe = false,
IsMacroType = false
});

// Add the function argument attributes.
// Gather list of attributes for this function args.
var argAttributes = new List<object>();
foreach (var arg in def.Args)
{
var argAttribute = new ExcelArgumentAttribute
{
Name = arg,
Description = ""
};

argAttributes.Add(argAttribute);
}

// Add the function args attributes.
argAttributesLists.Add(argAttributes);
}
}

ExcelIntegration.RegisterDelegates(delegates, functionAttributes, argAttributesLists);
}
catch (Exception ex)
{
Log.Error("EXCEPTION", ex);
MessageBox.Show(ex.Message, "AutoOpen Exception",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

public void AutoClose()
{

}

private static Dictionary<string, string> GetLuaScripts()
{
var scripts = new Dictionary<string, string>();

using (var workbook = Excel.ActiveWorkbook)
{
foreach (var part in workbook.CustomXMLParts)
{
// Load the XML and check what the root element name is.
var doc = new XmlDocument();
doc.Load(new StringReader(part.XML));
var root = doc.DocumentElement;
Debug.Assert(root != null);

switch (root.Name)
{
case "LuaScript":
var name = root.Attributes["name"]?.InnerText ?? "Unnamed";
var luaScript = root.InnerText;
Debug.Assert(!scripts.ContainsKey(name));
scripts.Add(name, luaScript);
break;
}
}

return scripts;
}
}

private static object RunFunction(string name, params object[] args)
{
try
{
Log.DebugFormat("RunFunction {0}", name);
var script = Scripts[name];
var result = script.Call(script.Globals[name], args);
return result.ToObject();
}
catch (Exception ex)
{
Log.Error("EXCEPTION", ex);
throw;
}
}

private static Delegate GetDelegate(string name, int argCount)
{
switch (argCount)
{
case 0: return (Func<object>)(() => RunFunction(name));
case 1: return (Func<object, object>)(arg => RunFunction(name, arg));
case 2: return (Func<object, object, object>)((arg1, arg2) => RunFunction(name, arg1, arg2));
case 3: return (Func<object, object, object, object>)((arg1, arg2, arg3) => RunFunction(name, arg1, arg2, arg3));
case 4: return (Func<object, object, object, object, object>)((arg1, arg2, arg3, arg4) => RunFunction(name, arg1, arg2, arg3, arg4));
case 5: return (Func<object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5) => RunFunction(name, arg1, arg2, arg3, arg4, arg5));
case 6: return (Func<object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6));
case 7: return (Func<object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7));
case 8: return (Func<object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
case 9: return (Func<object, object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9));
case 10: return (Func<object, object, object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10));
case 11: return (Func<object, object, object, object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11));
case 12: return (Func<object, object, object, object, object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12));
case 13: return (Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13));
case 14: return (Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14));
case 15: return (Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15));
case 16: return (Func<object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>)((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16) => RunFunction(name, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16));
// NOTE: It is not possible to register a function in Excel-DNA with more than 16 arguments.
default: throw new ArgumentOutOfRangeException($"Unsupported number of args: {argCount}");
}
}
}
}
31 changes: 31 additions & 0 deletions src/Lua Loader/Lua Loader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,61 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Antlr4.Runtime.Standard, Version=4.7.1.0, Culture=neutral, PublicKeyToken=e78b2c5abd1fcb3f, processorArchitecture=MSIL">
<HintPath>..\..\packages\Antlr4.Runtime.Standard.4.7.1.1\lib\net35\Antlr4.Runtime.Standard.dll</HintPath>
</Reference>
<Reference Include="ExcelApi, Version=1.7.3.0, Culture=neutral, PublicKeyToken=26cf6d85ba68fa54, processorArchitecture=MSIL">
<HintPath>..\..\packages\NetOffice.Excel.Net45.1.7.4.4\lib\net45\ExcelApi.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="ExcelDna.Integration, Version=0.34.6373.42344, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\ExcelDna.Integration.0.34.6\lib\ExcelDna.Integration.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="MoonSharp.Interpreter, Version=2.0.0.0, Culture=neutral, PublicKeyToken=921e73ce94aa17f8, processorArchitecture=MSIL">
<HintPath>..\..\packages\MoonSharp.2.0.0.0\lib\net40-client\MoonSharp.Interpreter.dll</HintPath>
</Reference>
<Reference Include="NetOffice, Version=1.7.3.0, Culture=neutral, PublicKeyToken=acf636d62c39f8f5, processorArchitecture=MSIL">
<HintPath>..\..\packages\NetOffice.Core.Net45.1.7.4.4\lib\net45\NetOffice.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="OfficeApi, Version=1.7.3.0, Culture=neutral, PublicKeyToken=7c1c3e9d16cace88, processorArchitecture=MSIL">
<HintPath>..\..\packages\NetOffice.Core.Net45.1.7.4.4\lib\net45\OfficeApi.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="VBIDEApi, Version=1.7.3.0, Culture=neutral, PublicKeyToken=a3637beacf571e8a, processorArchitecture=MSIL">
<HintPath>..\..\packages\NetOffice.Core.Net45.1.7.4.4\lib\net45\VBIDEApi.dll</HintPath>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AddIn.cs" />
<Compile Include="LuaFunctionDefinition.cs" />
<Compile Include="LuaFunctions.cs" />
<Compile Include="LuaFunctionsVisitor.cs" />
<Compile Include="Parser\LuaBaseVisitor.cs" />
<Compile Include="Parser\LuaLexer.cs" />
<Compile Include="Parser\LuaParser.cs" />
<Compile Include="Parser\LuaVisitor.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="Lua Loader-AddIn.dna" />
<None Include="packages.config" />
<None Include="Parser\Lua.g4" />
<None Include="Properties\ExcelDna.Build.props" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Expand Down
16 changes: 16 additions & 0 deletions src/Lua Loader/LuaFunctionDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Collections.Generic;

namespace LuaForExcel.LuaLoader
{
public class LuaFunctionDefinition
{
public string Name { get; }
public List<string> Args { get; }

public LuaFunctionDefinition(string name, List<string> args)
{
Name = name;
Args = args ?? new List<string>();
}
}
}
Loading

0 comments on commit 59e8701

Please sign in to comment.