I have been experimenting with using functions to make a mini programming language, but can't find out how to turn the function
add(1, 4)
into
(add, 1, 4)
So far I have this:
mem = {}
def inp(text):
return(input(text))
def store(name, value):
mem[str(name)] = value
def get(name):
return(mem[str(name)])
def ifel(con, exp1, exp2):
if con == True:
return(exp1)
else:
return(exp2)
def add(*args):
return(sum(args))
def sub(*args):
last = 0
for each in args:
last = last - each
return(last)
def dev(*args):
last = 0
for index, each in enumerate(args):
if index == 0:
last = each
else:
last = last / each
return(last)
def mul(*args):
last = 0
for index, each in enumerate(args):
if index == 0:
last = each
else:
last = last * each
return(last)
def power(*args):
last = 0
for index, each in enumerate(args):
if index == 0:
last = each
else:
last = pow(last, each)
return(last)
def root(*args):
last = 0
for index, each in enumerate(args):
if index == 0:
last = each
else:
last = pow(last, (1 / each))
return(last)
def say(t):
print(t)
print((5 * (9 ** 4)) / 3)
say(dev(mul(5, power(9, 4)), 3))
I would like to be able to write this as:
(say, (dev, (mul, 5, (power, 9, 5)), 3))
or
(say (dev (mul 5 (power 9 5)) 3))
How could I call a function with the first place in the parentheses? I got inspiration from Lisp syntax, and am trying to recreate my own version.
CodePudding user response:
You're either making a fairly basic mistake or attempting something very nuanced and difficult. :)
Assuming the former: You need to be clear about the difference between python syntax and your syntax. Functions in your syntax are generally not going to be python functions (there's some nuance there) - they'll be values, and you'll have something like an interp(...)
function that actually interprets them.
Because python functions are values, there can be some overlap here - (add, 3, 4)
could be treated as a tuple whose first value is a function. Pretending all your functions were binary operations, you could have something like:
def interp(someFunction, arg1, arg2):
someFunction(interp(arg1), interp(arg2))
with some other code to handle the base case of literal values. (Or do varargs and have literals as 0-argument functions that return the literal.)
More commonly you'd just have add
, etc, by something more akin to an enum, and interp
would match on each case:
def interp(term):
match term:
case(ADD, arg1, arg2): return interp(arg1) interp(arg2)
Generally speaking, this will be easier to serialize and inspect than having actual lambdas.
The Advanced Thing:
You're almost certainly not doing this, but there is such a thing as a "quoted DSL" which is a domain-specific language written as an extension of the host language. Implementing them is much more involved, but they have the advantage of benefiting from the development environment and features of the host language. (For instance, you can use them to get syntax completion and error highlighting in your DSL without taking on the very difficult task of writing such yourself.)