lmorg / murex

A smarter shell and scripting environment with advanced features designed for usability, safety and productivity (eg smarter DevOps tooling)
https://murex.rocks
GNU General Public License v2.0
1.46k stars 27 forks source link

A transpiler to convert murex scripts into bash scripts #281

Open lmorg opened 3 years ago

lmorg commented 3 years ago

Hey Author ;-)

If I can find the wherewithal to switch my native shell to one of these shell improvements such as yours I will. I think the productivity gains could be really nice. Of late, I've been forcing myself to learn idomatic bash for my shell scripting duties, though.

Perhaps a feature to think about would be to be able to transpile to idomatic bash so that people could script in your shell but ship portable bash. Then once more and more devs adopt your shell it could replace bash.

Source: https://news.ycombinator.com/item?id=26285735

supreetsingh10 commented 3 years ago

I believe I can solve this problem. I am a noob when it comes to open source. I like this project and I would love to work on this? How do I start working?

lmorg commented 3 years ago

Hi @supreetsingh10

Thank you for your interest.

This is a sufficiently distinct piece of work that I'm happy for you to be the judge on how you want to go about writing the transpiler. While I'm generally a fan of mono-repos, I don't see any particular necessity that such a compiler would need to form part of the same repository if you feel that would slow you down but if I were to start this myself I'd probably create a feature branch and work on it there so the code can be written independently to the murex shell while still allowing us to merge changes back in.

The murex parser does also create an AST ahead of interpreting each block of code. But the AST is only generated for a block at a time. eg

function example {
    # An example function
    if { $ENVVAR } then {
        out: 'foobar'
    }
    out: 'Finished!'
}

When that code is run function is executed with the parameters example and { ... } but the contents of { ... } isn't converted into ASTs until someone calls example elsewhere in the shell.

Then when example is executing, you'd have an AST of the commands inside said function but not any blocks that are associated with those functions. eg the AST would look something like this:

[
    {
        "Function": "if",
        "Parameters": [
            "{ $ENVVAR }",
            "then",
            "{\n        out: 'foobar'\n    }"
        ]
    },
    {
        "Function": "out",
        "Parameters": [
            "Finished!"
        ]
    }
]

(please note this is a mock rather than the real data representation of the ASTs. Parameters are stored differently to allow infixing of variables; and there also needs to be data shared about how pipelining (eg STDOUT et al) is chained).

So when if executes, the first conditional is then parsed and turned into ASTs and executed. Then the last parameter gets parsed and turned into ASTs if (and only if) the first conditional is true.

With murex, there's no distinction between text and code. It's up to commands to determine if they want to execute a parameter as code or not. So while the parser can be imported into your own Go project if you wanted, you'd still need to write logic around all the builtins to identify when a parameter is a code-block and when it is non-executable text (eg JSON). Though you'd need logic per builtin anyway if just to map each murex command to a Bash / POSIX counterpart.

Hope this helps.

lmorg commented 3 years ago

I've also added some documentation with regards to syntax / parsing rules that might help: https://murex.rocks/docs/GUIDE.parser.html

A breakdown of the core builtins is documented here: https://murex.rocks/docs/GUIDE.builtin-functions.html

I'm not going to pretend everything is documented nor about the quality of the documentation though...but it's getting there