~fabrixxm/formulap

Python-like language to Excel Formula compiler
cli: print full traceback on exception in debug mode
Allow macro defs in `if` bodies, in local context

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~fabrixxm/formulap
read/write
git@git.sr.ht:~fabrixxm/formulap

You can also use your local clone with git send-email.

#FormulaP

Python-like language to Excel Formula compiler

#Demo

tot = SUM(A1.A255)
if tot > 0:
 	f"Hai un positivo di {tot}"
elif tot == 0 and not B1 < 0:
	"Sei in pari"
else:
	"Sei sotto di brutto, zio."

compile to:

$ python -m formulap test.pyf
=IF(SUM(A1:A255)>0;CONCAT("Hai un positivo di ";SUM(A1:A255));IF(AND(SUM(A1:A255)=0;B1>=0);"Sei in pari";"Sei sotto di brutto, zio."))

#Try in browser

https://kirgroup.net/~fabrixxm/formulap/

#Language reference:

# string
# => "text"
"text"

# number 
# => 2
2

# decimal numbers
# Note: formula with decimals could be wrong in your excel regional settigs
# => 2.5
2.5

# cell reference
# => A1
A1

# range referenfce
# => A1:A9
A1.A9

# fixed cell reference
# => $A$1
{A1}

# fixed range reference
# => $A$1:$A$9
{A1.A9}

# conditional
# 'else' is optional, 'elif' is supported
# => IF(cond;...;...)
# => IF(cond1;...;IF(cond2;...;...))
if cond:
	...
else:
	...

if cond1:
	...
elif cond2:
	...
else:
	...


# Conditions
# => IF(A1=1;...)
# => IF(AND(A1>2;A1<=7);...)
# => IF(OR(A1<>B1;A1<>B2);...)
# => IF(NOT(A1=B1);...)
if A1 == 1:
	...

if A1 > 2 and A1 <= 7:
	...

if A1 != B1 or A1 != B2:
	...

if not A1 == B1:
	...

# Note: code like x < y < z is not supported

# Search in range
# => MATCH("val";B1.B100)
"val" in B1.B100


# Value in range
# => INDEX(A1:A10;5)
# => INDEX(A1:B10;5;2)
A1.A10[5]
A1.B10[5:2]


# function call
# => ABS(A1)
# => AVERAGE(A1;B1;A2:B2)
# => CEILING.MATH(A1;1)
ABS(A1)
AVERAGE(A1, B1, A2.B2)
CEILING.MATH(A1, 1)

# values concatenation
# => CONCAT("Total ";$A$1)
f"Total {[A1]}"

# Arithmetic operations
# => A1 + 2
# => A1 - 2
# => A1 * 2
# => A1 / 2
# => MOD(A1;2)
# => POWER(A1;2)
A1 + 2
A1 - 2
A1 * 2
A1 / 2
A1 % 2
A1 ** 2

# Pass
# `pass` is a no-op keyword
# => IF(A1;;"Is False")
if A1:
	pass
else:
	"Is False"

# Macros
# Macros are usefull to reduce code duplication.
# => IF(SUM(A1:A10)>0;CONCAT("Positive sum: ";SUM(A1:A10)))
tot = SUM(A1.A10)
if tot > 0:
	f"Positive sum: {tot}"

# can be defined inside if block
# => IF(A1=1;CONCAT("Res: ";A1+1))
if A1 == 1:
	b = A1 + 1
	f"Res: {b}"

# name is vaild only inside the block
if A1 == 1:
	b = A1 + 1
	pass
else:
	f"Res: {b}"  # CompilerException: Name 'b' is not defined

NOTE there must be ONE expression in if / else code blocks

This code is invalid:

if A1 < 2:
	A1 + 3
	A2 + 1