WingedSeal / jmc

A compiler for JMC (JavaScript-like Minecraft Function), a mcfunction extension language for making Minecraft Datapack
https://jmc.wingedseal.com
MIT License
64 stars 8 forks source link

Python input formatter built into the compiler #51

Closed w00tyd00d closed 6 months ago

w00tyd00d commented 10 months ago

Description

The addition of a built-in python code string formatter for the JMC.python module would allow users to have an arbitrary amount of indentation whitespace when coding python in a JMC file. This would prevent always needing to format the python code to align directly with the left-most column.

It should be noted that all subsequent indentation should still align with python's indentation protocol, but as long as each line is indented relative to the same uniform length, it should still work just fine.

Example of current implementation:

image

Example of desired implementation:

image

I've written up a mock-up example of how this formatting function could be written, although of course use your own discretion.

python_string = """
        a = 2
        def hello():
            print("Hello!")
            print("World!")
        hello()
"""

def indent_format(inp: str) -> str:
    _type = _minimum = None
    res = ""

    for ln in inp.splitlines():
        if not ln:
            continue

        _count = 0
        for i,c in enumerate(ln):
            if c == " " or c == "\t":
                _type = c if _type is None else _type
                if _type != c:
                    raise IndentationError("Mismatch use of spaces and tabs")
                _count += 1
            else:
                _minimum = _count if _minimum is None else _minimum
                if i < _minimum:
                    raise IndentationError("Left indentation not aligned properly")
                break

        res += ln[_minimum:] + "\n"

    return res

fmt_string = indent_format(python_string)

print("Unformatted code:")
print(python_string)

print("Formatted code:")
print(fmt_string)

print("Executing code:")
exec(fmt_string)
WingedSeal commented 6 months ago

Implemented in https://github.com/WingedSeal/jmc/commit/037d50fe56368d493e2c260ed2b2018ee6cf5af4

WingedSeal commented 6 months ago
    def clear_indent(self, string: str, indent: str) -> str:
        if not string:
            return string
        if string.startswith(indent):
            return string[len(indent):]
        raise JMCSyntaxException(
            "Invalid indentation when trimming indentation",
            self.raw_args["pythonCode"].token,
            self.tokenizer, suggestion=string, col_length=False, display_col_length=False)

    def call(self) -> str:
        if not self.args["pythonCode"]:
            return ""

        python_lines = self.args["pythonCode"].split("\n")
        indent = python_lines[0][:len(
            python_lines[0]) - len(python_lines[0].lstrip())]
        if not indent:
            python_code = self.args["pythonCode"]
        else:
            python_code = "\n".join(self.clear_indent(line, indent)
                                    for line in python_lines)

Ended up using different implementation