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

  1. Clone the https://github.com/VFPLang/vfpl repository
  2. Optionally, run cargo test
  3. 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

NameParametersReturns
println(x as <Any>)absent

Other

time

Returns the current time in unix milliseconds

NameParametersReturns
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 and False 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.