can I use static classes in Nlua?

295 views Asked by At

First and formost, I am not a native english speaker, sorry for any communication errors this question might emit. Also, couldn't really understand some of your guidelines for asking a question, so followed what I understood of it. As a consequence, please tell me if I violate any custom.

background

I have been making an app that trys to implement something like xaml hot reload for code. As I can't directly make it with compiled languages like c#, I decided I will rely on a third-party scripting language, thinking I could write a bit of my app logic in it, then deploy it faster to the device because I would only need to press a button to send the code across. So, one can think of it like the expo client in the react-native world.

I decided to use lua as the scripting language, not only because it's very simple to write code in, but also because it has a nice binding for .net, I'm talking about Nlua, of course. And, as ironpython3 is not ready for production yet, what choices do I have honestly?

the problem I am facing

So, as I have to bind some xamarin.essentials stuff to make it easier for me to write the lua side of the app, I have to interface with some objects that have, among many other things, static methods. I thought it would be very intuitive and straight-forward to use static methods in Nlua, though it seemns I was mistaken so far.

how to reproduce the issue

  1. So, in visual studio, create a xamarin.forms project, name it however you like, it doesn't matter.
  2. Next, add the Nlua nuget package to all your projects. It needs to be integrated in all of them because it, like some xamarin.forms plugins, relies on native libraries to work, so they need to be included in the proper places for the OS to find them. This is automatically done by the package when installed in a supported project type, so do that.
  3. Add the Nlua namespace to the top of the codebehind of your MainPage content page, like this:
    using NLua;  
  1. For easy use going forward, add a class level property to the MainPage class, like this:
    Lua LuaState{get; set;}  
  1. Now, initialise it in the page constructor, for simplicity's sake:
    LuaState = new Lua();
  1. In the xaml of the page, add an edit field in which to input the text and a button to be able to run it:
    <StackLayout>
        
        <Editor
                x:Name="edCode"
            Placeholder="type your code here"    
            />

            <Button
                x:Name="btnStartCode"
                Text="compile"
                HorizontalOptions="End"
                VerticalOptions="EndAndExpand"
                Clicked="btnStartCode_Clicked"
            />
    </StackLayout>
  1. In the button clicked handler, add code to load the things in the edit box, then make nlua execute it, something like this:
    async void btnStartCode_Clicked(object sender, EventArgs e)
    {
        try
        {
            LuaState.DoString(edCode.Text);
        }
        catch (Exception exc)
        {
            await DisplayAlert("error", $"A fatal error was incountered while running your code\nException details:\n\tException type: {exc.GetType().ToString()}\n\terror message from interpreter:{exc.Message}.", "OK");
        }

    }
  1. Now, add a new class, call it however you want, I called mine tts. Delete everything inside the file vs generated, then add the following content in it:
    using System;
    
        using System.Collections.Generic;
        using System.Text;
        using Xamarin.Essentials;
        namespace autoaccess.scriptables
        {
            public class tts
            {
                public async void speak(params object[] messages)
                {
                    foreach(var message in messages)
                    {
                        if (message == null)
                        {
                            await TextToSpeech.SpeakAsync("null value");
                            return;
                        }
                        await TextToSpeech.SpeakAsync(message.ToString());
                    }
                }
            }
        }
  1. Back in the main content class, either in the constructor or in a dedicated method called from the constructor, register the lua type, like this:
LuaState["text_to_speech"=typeof(TextToSpeech);  

expected behaviour

It should speak the params given to the function if using . notation, e.g tts.speak("hello world")

actual behaviour

When a call with . notation is executed, the lua interpreter throws the following error:

[string "chunk"]:1: attempt to call a nil value (field 'speak').

what I tryed so far

  1. First, I tried to get the type of the static object with the typeof keyword, then registering it directly as a lua accessible value, like I have shown above. This threw an error, as you could see.
  2. If, for example, I try to invoke the type like a function to get a new object like shown in the documentation, it fails again.
  3. If I use the State.UseClrPackage(); method, then importing the stuff manually, it doesn't work either, can't access any of the methods or variables, lua says they're all "nill".

Note: Upon closer inspection, I see the variable lua sees is actually the real clr type, since the str function of lua shows the full type qualifier, as present in .net. However, the type function returns userdata.

  1. Instead of making the entire type be visible to lua as it is, I tried to make a lua table with the name, then add key-value pairs where the key is the method name, and the value is a static object's method, as returned by typeof(thing).GetMethod(MethodName) cast to the LuaFunction type. This worked to a certain point, though it quickly grue too complicated and overwhelmingfor me, so I stopped using that approach, as my codebase grue very intangled.

  2. Another thing I tryed is to make proxi objects for static ones in which nothing is actually static, even though the methods return void and are acting on static properties.

That, again, kind of worked, but I then necessarely had to use the object:thing, when the norm for static methods is object.thing. If I tryed to use the object.thing notation, I got a pretty long error, though I'm sure it's content is not relevant to the problem at hand, so not going to include it here.

conclusion

So, officially, I tryed everything I could, even did a search on here to see if I would be able to find anything to enlighten me, though no luck, unfortunately. Now, what do you recommend I use? Is there something I overlooked somewhere?

0

There are 0 answers