VFPL
NOTE: VFPL is very much work in progress and several major features are still missing
VFPL is formal, politely verbose programming language for building next-gen reliable applications.
It does not aim to be the most expressive and simple, but attempts to scale no matter how large the codebase and ambitions of the programmer are. It's main target is the large enterprise audience, but it is a good fit for smaller programs as well.
VFPL is imperative and statically and strongly typed (although the current implementation is dynamic). It is also null safe and has 4
different null values (absent
, null
, undefined
and novalue
), because not every absence is created equal.
The hello world looks like this:
please call println with the argument "Hello, World!" as x.
please go to sleep.
VFPL is not case sensitive, but there are several conventions around naming.
Every function must explicitly return a value, absent
is the recommended value if there is none returned, but the other null values can be
used as well.
The program must also always be terminated by a please go to sleep.
.
Install
You can either download the binaries from the releases or build the latest main branch yourself.
Building it yourself
Dependencies
- Rust needs to be installed
Steps
- Clone the https://github.com/VFPLang/vfpl repository
- Optionally, run
cargo test
- Run
cargo install --path .
Syntax
VFPL has a deliberately verbose syntax, to provide the maximal explicitness and reduce accidental mistakes
Every statement in VFPL starts with a please
and ends with a dot .
.
Variables are declared and initialized the following way:
please initialize variable x as Integer with the value of 3.
They can be reassigned using a set
statement
please set the variable X to the value of 6
Here we use uppercase for the variable name, which, according to convention, suggests a mutable variable.
Loops are done using while loops
please repeat while True do
please end while.
Conditionals are done using check
and otherwise
please check whether True, then do
please end check.
please check whether False, then do
otherwise,
please end check
You can write comments using the HTML syntax
<!-- Hi, this is a comment -->
Functions can be created like this
please create function hello_world with no parameters that returns absent
<!-- ... -->
please return absent from the function.
please end function hello_world.
please create function print_int with the parameter x as Integer that returns null
<!-- ... -->
please return null from the function.
please end function print_int.
please create function add_ints with the parameters x as Integer and y as Integer that returns Integer
please return (add x to y) from the function.
please end function add_ints.
please create function add_3_ints with the parameters x as Integer, y as Integer and z as Integer that returns Integer
please return (add x to (add y to z)) from the function.
please end function add_3_ints.
And then called using similar syntax, where the parameter names have to be specified in the correct order
please call hello_world with no arguments.
please call print_int with the argument 5 as x.
please call add_ints with the arguments 5 as x and 6 as y.
please call add_3_ints with the arguments 5 as x, 6 as y and 7 as z.
Grammar
The grammar is written in BNF
// VFPL Grammar
////// Base rules
program ::= body
body ::= (statement)*
////// Other rules
typed-ident ::= ident "as" type
value-ident ::= expr "as" ident
ident ::="regexp:\w"+
// type
type ::= ident | nullable
nullable ::= "absent" | "null" | "novalue" | "undefined"
////// Item rules
// function
fn-decl ::= "create function" ident "with" fn-params fn-return
body
"please end function" ident
fn-params ::= fn-no-params | fn-single-params | fn-multi-params
fn-no-params ::= "no parameters"
fn-single-params ::="the parameter" typed-ident
fn-multi-params ::="the parameters" typed-ident ("," typed-ident)* "and" typed-ident
fn-return ::="that returns" type
// structure
struct-def ::= "define structure" ident "with" struct-fields? "please end define"
struct-fields ::= struct-field (("," struct-field)* "and" struct-field)?
struct-field ::= "the field" typed-ident
////// Statement rules
statement ::= "please" (
fn-decl |
struct-def |
variable-init |
variable-set |
if |
while |
break |
return |
terminate |
expr
) "."
variable-init ::= "initialize variable" typed-ident "with the value of" expr
variable-set ::= "set the variable" ident "to the value of" expr
// control flow
if ::= if-part "please end check"
if-part ::= "check wheter" expr ", then do" body (else)?
else ::= "otherwise, " (if-part | body)
while ::= "repeat while" expr "do"
body
"please end while"
break ::= "break out of this while"
return ::="return" expr "from the function"
terminate ::= "go to sleep"
////// Expression rules
expr ::= comparison
comparison ::= term ((
"does not have the value" |
"has the value" |
"is greater than" |
"is less than" |
"is greater or equal than" |
"is less or equal than"
) comparison)?
term ::= add | subtract | factor
add ::= "add" factor "to" term
subtract ::= "subtract" factor "from" term
factor::= multiply | divide | modulo | call-expr
multiply ::= "multiply" call-expr "with" factor
divide ::= "divide" call-expr "by" factor
modulo ::= "take" call-expr "modulo" factor
call-expr ::= call | primary-expr
primary-expr ::= "(" expr ")" | literal
// function call
call ::= "call" ident "with" call-args
call-args ::= call-no-arg | call-single-arg | call-multi-arg
call-no-arg ::="no arguments"
call-single-arg ::="the argument" value-ident
call-multi-arg ::="the arguments" value-ident ("," value-ident)* "and" value-ident
// literals
literal ::= struct-literal |nullable | "regexp:\".*\"" | number | "true" | "false" | ident
number ::= int | float
float ::= "-"? "regexp:\d"+ "."? "regexp:\d"+
int ::= "-"? "regexp:\d"+
// struct literal
struct-literal ::= ident "with" struct-lit-fields
struct-lit-fields ::= struct-lit-no-fields | struct-lit-fields-some
struct-lit-no-fields ::= "no fields"
struct-lit-fields-some::= value-ident (("," value-ident)* "and" value-ident)?
List of keywords
Normal keywords
Normal keywords cannot be used as identifiers
absent
and
as
break
call
check
create
do
end
false
function
initialize
not
novalue
null
or
otherwise
please
repeat
return
structure
then
this
true
undefined
variable
whether
while
Conditional keywords
Conditional keywords can be used as identifiers
add
argument
arguments
by
div
does
equal
field
fields
from
go
greater
has
have
is
less
mod
mul
no
of
out
parameter
parameters
returns
set
sleep
sub
take
than
that
the
to
value
with
Standard library
The standard library is very minimal for now, but it will be extended in the future
IO
Functions for doing IO
println
Prints any value to the standard output. Has the special type <Any>
, meaning it can take any value
Name | Parameters | Returns |
---|---|---|
println | (x as <Any>) | absent |
Other
time
Returns the current time in unix milliseconds
Name | Parameters | Returns |
---|---|---|
time | () | Integer |
Best practices
- VFPL file names are written in
PascalCase
with the extension.vfpl
- Functions are in
snake_case
- Variables are also in
snake_case
- Mutable variables start with an
Uppercase
letter - Indentation is done with 4 spaces
- Subexpressions are wrapped in parentheses
add 6 to (multiply 5 with 7)
True
andFalse
are referenced in uppercase- Types (expect the null values) are
Uppercase
Examples
Fizzbuzz
please initialize variable Number as Integer with the value of 1.
please repeat while Number is less or equal than 100 do
please check whether (take number modulo 15) has the value 0, then do
# FizzFuzz
please call println with the argument "FizzBuzz" as x.
otherwise, check whether (take number modulo 5) has the value 0, then do
# Buzz
please call println with the argument "Buzz" as x.
otherwise, check whether (take number modulo 3) has the value 0, then do
# Fizz
please call println with the argument "Fizz" as x.
otherwise,
please call println with the argument number as x.
please end check.
please set the variable Number to the value of (add 1 to Number).
please end while.
please go to sleep.