coderofsalvation / powscript

transpiler written in bash: painless shellscript, indentbased, coffee for the shell with hipster-sparkles v1 BETA LANDED 🎉🎉🎉🎉 thanks fcard!
http://powscript.isvery.ninja/
Other
159 stars 13 forks source link

Macros? #42

Open fcard opened 7 years ago

fcard commented 7 years ago

An AST based compiler allows lisp-like macros to be implemented.

It would be very handy in implementing the remaining and future powscript extensions, as well as dealing with the fact that eval doesn't work with powscript code by introducing alternatives.

yay/nay?

coderofsalvation commented 7 years ago

yay! macros rock.

fcard commented 7 years ago

How about this syntax?

GENSYM_COUNT=0
macro gensym(var) # create an unique name
  GENSYM_COUNT+=1
  local name<-create-ast name "__gensym_name_$GENSYM_COUNT"
  return value :{local ~var=~name}

macro swap(x y) # swap the values of the given variables
  gensym tmp
  return code
    ~tmp=~x
    ~x=~y
    ~y=~tmp

a=1
b=2
swap a b
echo $a $b #> 2 1

Suggestions/criticism as always are welcome.


Explanation:

First off, macros manipulate data, not strings, so in order to avoid having users have to do all the things I did with setvar and noshadow in the compiler, I've come up with some syntactic sugar for returning and putting data in variables.

add(a b)
  return value $(math a+b)

x<-add 1 2
echo $x #> 3

Which would translate to Bash as something like:

add() {
  local a="${1}" b="${2}"
  printf -v "$__return_value_add" '%s' $((${a}+${b}))
}

__return_value_add=__return_value_0 add 1 2 
x=$__return_value_0
echo $x


The second bit of syntax is the code literal, :{some ~code}, which creates and returns an AST. ~ is what I used for interpolation/substitution. (e.g. v=:{10}; x=:{echo ~v} is equivalent to x=:{echo 10})

code=:{local x=~value} would be equivalent to:

ast:make code local ''
ast:make __ast_child_0 assign 'x' $value
ast:push-child $code $__ast_child_0

return code is a way to make returning multi-line ASTs simpler and more powscripty, otherwise it would need to be something like:

return value :{
  block
    ~tmp=~x
    ~x=~y
    ~y=~tmp
}


The rest I imagine is straight forward:

macro m(a b c)
  ...

creates a macro m that takes a, b, and c as arguments, which behaves like a function, except it's executed in the compilation process as it's found.


Just to give some perspective on why manipulating data is better with printf -v than with echo:

add_echo() {
  echo $((${1} + ${2}))
}
$ time for i in {1..50000}; do x=$(add_echo 1 2); done

real    0m58.490s
user    0m34.875s
sys 0m25.199s
$  time for i in {1..50000}; do __return_value_add=__return_value_0 add 1 2; x=$__return_value_0; done

real    0m1.157s
user    0m1.156s
sys 0m0.000s

So having syntax for this strategy of data passing would be nice in general.

fcard commented 7 years ago

Alternatively instead of another type of assignment

x<-f x y

we could have a "value substitution" syntax

x=$[f x y]

Which has a number of advantages like allowing more than one to be passed to a command and being more "shell-like", but I wonder if it wouldn't be confusing.

If this requires more discussion I will open another issue, although macros require either functionality to be easy to use.

coderofsalvation commented 6 years ago

last time I've used macros was with C (long time ago). I like your last 'value substitution'-example, it definately looks 'shell-like'. If this can speed up shellscripts, and it looks 'shell-ish': Im all for it.