-1

I have an S-expression in Python which I need to convert it into tuple with operators (add,multiply) inside the semi-colon ie as a string.

I have the following code snippet:

This code works fine, but the requirement of the work is that the user doesnot input tuple like ('add', ('multiply', 3, 4), 5) instead pass an s-expression like "(add (multiply 2 3) 2)" from the system command line.

The command to put an input will be like this:

python calc.py "(add (multiply 2 3) 2)"

For now I have already defined expression in the code, but the users will be giving the expression and we fetch it using sys.argv . So how to convert the user input "(add (multiply 2 3) 2)" to ('add', ('multiply', 3, 4), 5) so that I can use the above code easily.

Update:

I tried the following code but it didnt give the desired result.

def from_expression_string(expression_string):

    tokens = expression_string.strip().split()
    #['(add', '(multiply', '2', '3)', '2)']
    stack = []
    for token in tokens:
        if token == '(':
            # print("hello")
            pass
        elif token == ')':
            args = [stack.pop(), stack.pop()]
            stack.append((stack.pop(), *reversed(args)))
        else:
            try:
                stack.append(int(token))
            except ValueError:
                stack.append(token)
    return stack[0]

The output of this snippet gives (add. The above code seems easy to understand but is not working.

Reactoo
  • 916
  • 2
  • 12
  • 40

1 Answers1

2

You can tokenize the input s-expression with a regex of an alternation pattern, iterate through the tokens while appending each token to a stack, but if the token is a left parenthesis, push the current size of the stack to another stack recording the stack positions of left parentheses, and if the token is a right parenthesis, pop the left parentheses stack for the nearest position of a left parenthesis and replace the items from the position with a new sub-list of these items. As long as the s-expression is well-formed, the stack should be left with a single item of the desired list output when all tokens are processed:

import re

def convert(s):
    stack = []
    lefts = []
    for token in re.findall(r'\w+|[()]', s):
        if token == ')':
            index = lefts.pop()
            stack[index:] = stack[index:],
        elif token == '(':
            lefts.append(len(stack))
        else:
            if token.isdigit():
                token = int(token)
            stack.append(token)
    return stack[0]

print(convert('(add (multiply 2 3) 2)'))

This outputs:

['add', ['multiply', 2, 3], 2]

Demo: https://replit.com/@blhsing/ProfitableColorfulProlog

blhsing
  • 91,368
  • 6
  • 71
  • 106
  • @blhsing I have added my code, but didnt give the correct output. If you can modify my update, I will accept your answer. – Reactoo Feb 14 '23 at 05:33
  • @Reactoo Why you are using `str.split` instead of `re.findall`? With `str.split` you can't separate parentheses from the words. – blhsing Feb 14 '23 at 05:37
  • can you write findall expression without using regex, i dont really understand regex. – Reactoo Feb 14 '23 at 05:41