23 Dec, ’21

Evennia script parsing mk alpha

by Cal

I asked for some advice on the Evennia discord and got some useful directions, which got me started enough that I've put together a rough framework for the parser.

It still needs all the object-handling code that Evennia uses - mostly searching stuff to get the dbrefs for the relevant objects involved - but I think I've nailed the logic flow. It's really, really simplified, on purpose. The final player scripts should look something like this:

if v is !object:
run -o !object exec_a
run -t v exec_a
else:
if v not @player:
run -o !object exec_b

I want to make it so that I can do an else if condition: line but I have to do a special handler on the else line for that and I just haven't gotten to it yet. More importantly than that, I also want to set up the option to test the outcome of running a subroutine within an "if" statement, which I also haven't gotten to yet.

Here's all the framework I have so far:

def parse_eq(a,b):
    if a == b:
        return True

    if a.startswith("@"):
        # search players
    elif a.startswith("!"):
        # search objects
    elif a == "v":
        # use caller - have to add caller as param to all these

    if b.startswith("@"):
        # search players
    elif b.startswith("!"):
        # search objects
    elif b == "v":
        # use caller - have to add caller as param to all these

    return a == b

def parse_ineq(a,b):
    if a == b:
        return False

    if a.startswith("@"):
        # search players
    elif a.startswith("!"):
        # search objects
    elif a == "v":
        # use caller - have to add caller as param to all these

    if b.startswith("@"):
        # search players
    elif b.startswith("!"):
        # search objects
    elif b == "v":
        # use caller - have to add caller as param to all these

    return a != b


def parse_ifthen(lines):
    # validate structure
    line = lines[0].lower().strip()
    if line.startswith("if") and line.endswith(":"):
        # basic syntax check passed

        # find first else block
        elsepos = 1
        while not lines[elsepos].lower().strip() == "else" and elsepos < len(lines):
            elsepos = elsepos + 1

        ents = line.split()
        i = 1
        # evaluate "if" condition
        while i < len(ents):
            a = ents[i]
            b = ents[i+2]
            eq = ents[i+1]
            if eq == "is":
                # equality check
            elif eq = "not":
                # inequality check
            else:
                # not *quality check
                return "ERROR: invalid condition"

            # parse out and/or connectors
            if len(ents) > i+3:
                andor = ents[i+3]
                if andor != "and" and andor != "or":
                    # not an and/or statement, invalid script
                    return "ERROR: invalid logic operator"
                if andor == "and" and comp == False:
                    # "and" condition fails
                    break
                elif andor == "or" and comp == True:
                    # "or" condition succeeds
                    break
                else:
                    i = i+4

        if comp:
            # evaluate "then" result
            i = 1
            while i < elsepos:
                line = lines[i].lower().strip()
                # is it another "if"?
                if line.startswith("if"):
                    # RECURSION TIME
                    err = parse_ifthen(lines[i:elsepos])
                    if err:
                        return err
                    break

                # otherwise parse each line as a command
                elif line.startswith("run "):
                    # do the stuff
                else:
                    return "ERROR: invalid command"

                i = i+1

        elif elsepos < len(lines):
            # evaluate "else" result
            i = elsepos + 1
            while i < len(lines):
                line = lines[i].lower().strip()
                # is it another "if"?
                if line.startswith("if"):
                    # RECURSION TIME
                    err = parse_ifthen(lines[i:])
                    if err:
                        return err
                    break

                # otherwise parse each line as a command
                elif line.startswith("run "):
                    # do the stuff
                else:
                    return "ERROR: invalid command"

                i = i+1

    return False # return value is error so False means no error


def pc_script(text):
    lines = text.splitlines()

    err = parse_ifthen(lines)
    if err:
        #handle error