Pepper programming language: Home | Explicit Ownership | Examples

Below, where you see "(fact)" you know the prototype Pepper compiler does produce the output shown when run with these arguments. Where you see "(fiction)" you know the compiler fails on this input, or produces different output from what is shown.

Over time, hopefully, the fiction will gradually be replaced by fact.

Hello, world!

hello.pepper
print( "Hello, world!" )
(fact)
$ pepper -o hello.pepperlexed hello.pepper
hello.pepperlexed
0001:0001     SYMBOL(print)
0001:0006     LPAREN
0001:0008     STRING(Hello, world!)
0001:0024     RPAREN
0001:0025    NEWLINE
(fact)
$ pepper -o hello.pepperparsed hello.pepperlexed
hello.pepperparsed
PepFunctionCall(PepSymbol('print'),(PepString('Hello, world!'),))
(fact)
$ pepper -o hello.cpp hello.pepperparsed
hello.cpp
#include <stdio.h>

int main( int argc, char* argv[] )
{
    printf( "Hello, world!\n" );

    return 0;
}
(fact)
$ pepper hello.pepper
Hello, world!

Loops

for_in_range.pepper
import sys

for int n in range( 0, len( sys.argv ) ):
    print( n )

for int n in range( 3, len( sys.argv ), 2 ):
    print( n )
(fact)
$ pepper -o for_in_range.cpp for_in_range.pepper
for_in_range.cpp
#include <stdio.h>

int main( int argc, char* argv[] )
{
    for( int n = 0; n < argc; ++n )
    {
        printf( "%d\n", n );
    }
    for( int n = 3; n < argc; n += 2 )
    {
        printf( "%d\n", n );
    }

    return 0;
}
(fact)
$ pepper ./for_in_range.pepper 2 3 4 5 6
0
1
2
3
4
5
3
5

Add at compile time

addatcompiletime.pepper
print( 1 + 2 )

# Because all the arguments to "+" are known at compile time,
# it can execute at compile time.

# "print" can never execute at compile time because it has
# a runtime side effect.
(fact)
$ pepper -o addatcompiletime.pepperparsed addatcompiletime.pepper
addatcompiletime.pepperparsed
PepFunctionCall(PepSymbol('print'),(PepPlus(PepInt('1'),PepInt('2')),))
(fact)
$ pepper -o addatcompiletime.cpp addatcompiletime.pepperparsed
addatcompiletime.cpp
#include <stdio.h>

int main( int argc, char* argv[] )
{
    printf( "%d\n", 3 );

    return 0;
}
(fact)
$ pepper addatcompiletime.pepper
3

Defining functions

deffn.pepper
def int plus1( int i ):
    return i + 1

print( plus1( 2 ) )
(fact)
$ pepper -o deffn.pepperparsed deffn.pepper
deffn.pepperparsed
PepDef(PepSymbol('int'),PepSymbol('plus1'),((PepSymbol('int'), PepSymbol('i')),),(PepReturn(PepPlus(PepSymbol('i'),PepInt('1'))),))
PepFunctionCall(PepSymbol('print'),(PepFunctionCall(PepSymbol('plus1'),(PepInt('2'),)),))
(fiction)
$ pepper -o deffn.cpp deffn.pepperparsed
deffn.cpp
#include <stdio.h>

int main( int argc, char* argv[] )
{
    printf( "%d\n", 3 );

    return 0;
}
(fact)
$ pepper deffn.pepper
3

Overloading functions (at compile time)

overload_function.pepper
def int double( int x ):
    return x * 2

def float double( float x ):
    return x * 2.0

print( double( 6 ) )

print( double( 1.2 ) )
(fact)
$ pepper -o overload_function.cpp overload_function.pepper
overload_function.cpp
#include <stdio.h>

int main( int argc, char* argv[] )
{
    printf( "%d\n", 12 );
    printf( "%f\n", 2.4 );

    return 0;
}
(fact)
$ pepper ./overload_function.pepper
12
2.400000

Overloading functions (at run time)

overload_runtime.pepper
import sys

def void do_it( int x ):
    print( "int: " + x )

def void do_it( bool x ):
    print( "bool: " + x )

do_it( len( sys.argv ) > 2 )
do_it( len( sys.argv ) )
do_it( len( sys.argv ) )
do_it( len( sys.argv ) > 3 )
(fiction)
$ pepper -o overload_runtime.cpp overload_runtime.pepper
overload_runtime.cpp
#include <stdio.h>

void do_it( bool x )
{
    printf( "bool: %s\n", (x ? "true" : "false") );
}

void do_it_pep_1( int x )
{
    printf( "int: %d\n", x );
}

int main( int argc, char* argv[] )
{
    do_it( (argc > 2) );
    do_it_pep_1( argc );
    do_it_pep_1( argc );
    do_it( (argc > 3) );

    return 0;
}
(fiction)
$ pepper ./overload_runtime.pepper foo bar
bool: true
int: 3
int: 3
bool: false

Functions as values

fn_as_value.pepper
def int plus1( int i ):
    return i + 1

def int apply_twice( function( int, ( int, ) ) fn, x ):
    return fn( fn( x ) )

print( appy_twice( plus1, 2 ) )
(fiction)
$ pepper fn_as_value.pepper
4

Pure Functions

pure_fn.pepper
def(pure) int cubed( int i ):
    return i * i * i

answers = [ cubed( n ) for n in range( 1, 4 ) ] # could execute in parallel

print( answers )
(fiction)
$ pepper pure_fn.pepper
[1, 8, 27]

If expressions

if_expression.pepper
import sys

def string respond_to_name_1( string name ):
    return if name == "Andy":
        "My name!"
    else:
        "Not my name!"

def string respond_to_name_2( string name ):
    return (
        if name == "Andy":
            "My name!"
        else:
            "Not my name!"
        )

def string respond_to_name_3( string name ):
    # Maybe this way too?
    return:
        if name == "Andy":
            "My name!"
        else:
            "Not my name!"

print( respond_to_name_1( sys.argv[1] ) )
print( respond_to_name_2( sys.argv[1] ) )
print( respond_to_name_3( sys.argv[1] ) )
(fiction)
$ pepper ./if_expression.pepper Andy
My name!
My name!
My name!

$ pepper ./if_expression.pepper Percival
Not my name!
Not my name!
Not my name!

Classes

classes.pepper
class MyClass:
    def auto __init__( auto self, int x, int y ):
        vars:
            int self.sum = x + y
        print( "Creating new MyClass" )

    def(const) int Sum( auto self ):
        return self.sum

    def void Add( auto self, int toadd ):
        self.sum += toadd

    def(static) string Author( auto self ):
        return "Andy"

auto mycls = MyClass.init( 2, 3 )
mycls.Add( 1 )

print( mycls.Sum() )
print( MyClass.Author() )
(fiction)
$ pepper classes.pepper
Creating new MyClass
6
Andy

Closures

closure.pepper
def auto make_plusn( int n ):
    def int toreturn( int i ):
        return n + i
    return toreturn

auto plus6 = make_plusn( 6 )

print( plus6( 5 ) )
(fiction)
$ pepper -o closure.cpp closure.pepper
closure.cpp
#include <cstdio>

struct make_plusn_pep_f_toreturn
{
private:
    int n;
public:
    make_plusn_pep_f_toreturn( int n )
    : n( n )
    {
    }

    int operator()( int i )
    {
        return n + i;
    }
};

make_plusn_pep_f_toreturn make_plusn( int n )
{
    return make_plusn_pep_f_toreturn( n );
}

int main( int argc, char* argv[] )
{
    make_plusn_pep_f_toreturn plus6 = make_plusn( 6 );
    printf( "%d\n", plus6( 5 ) );

    return 0;
}
(fiction)
$ pepper closure.pepper
11

Compile-time for loops

compile_time_for.pepper
for int i in range( 4 ):
    def int symbol( "minus" + str( i ) )( int z ):
        return z - i

assert_equal( minus2( 7 ), 5 )
assert_equal( minus3( 6 ), 3 )
(fiction)
$ pepper compile_time_for.pepper

Meta-functions

meta_fn.pepper
def auto make_sort_pair( type T ):
    def ( T, T ) sort_pair( T x, T y ):
        if x > y:
            return ( y, x )
        else:
            return ( x, y )
    return sort_pair

sort_ints = make_sort_pair( int )

print( sort_ints( 4, 3 ) )
(fiction)
$ pepper meta_fn.pepper
(3, 4)

Templating on ordinary (non-type) values

template_nontypes.pepper
template( type T, int i ):
    def T addI( T x ):
        return x + i

print( addI( float, 3 )( 2.8 ) )
print( addI( int,   3 )( 4 ) )
(fiction)
$ pepper templates.pepper
5.8
7

Templates

templates.pepper
template( type T ):
    def T add1( T x ):
        return x + 1

print( addI(auto)( 2.8 ) )
print( addI(auto)( 4 ) )
(fiction)
$ pepper templates.pepper
3.8
5
implicit_templates.pepper
import random
import sys

def index_t partition( implements(IIndexable) list, index_t start, index_t end,
        index_t pi ):
    # Swap the pivot to the end for now
    index_t last = end - 1
    list_swap( list, pi, last )
    value_type(list)* pivot_item = list[last]

    # Compare each value to the pivot and if smaller, put it at the beginning
    index_t counter = start
    for index_t idx, value_type(list)* item in enumerate(
            list[start:end-1], start ):
        if item < pivot_item:
            list_swap( list, idx, counter )
            ++counter

    # Put the pivot back in the middle
    list_swap( list, last, counter )

    # Return where the pivot ended up
    return counter



def index_t choose_pivot_index( index_t start, index_t end ):
    return random.randrange( start, end )


# This function is "templated" i.e. different code is generated depending on
# what types you call it with, but there is no need to say "template" in
# its definition: the fact that list's type is unknown here, but when we
# call the function it is known, means that we generate a version of this
# function for that known type.
def void quick_sort( implements(IIndexable) list, index_t start, index_t end ):
    if end - start <= 1:
        return
    pi = choose_pivot_index( start, end )
    new_pi = partition( list, start, end, pi )
    quick_sort( list, start, new_pi )
    quick_sort( list, new_pi + 1, end )


def void sort( implements(IIndexable) list ):
    quick_sort( list, 0, len( list ) )

vector(int) nums = [ convert_to(int)( arg ) for arg in argv[1:] ]

# This line causes a "vector(int)" version of sort to be created
sort( nums )

print( nums )
(fiction)
$ ./implicit_templates.pepper 4 10 7 8 16 2
[2, 4, 7, 8, 10, 16]

"Token pasting"

token_pasting.pepper
def string symbol( "poo" + "h" )():
    return "winnie"

print( pooh() )
(fiction)
$ pepper token_pasting.pepper
winnie

Precalulating a lookup table

compile_time_fibonacci.pepper
def int next_fib( uint k_minus_1, uint k_minus_2 ):
    return k_minus_1 + k_minus_2


template( size_t how_many ):
    def array(uint,how_many) calc_fibs():
        array(uint,how_many) ret = array(uint,how_many).init( 0 )
        if how_many < 1:
            return ret
        ret[0] .= 1
        if how_many < 2:
            return ret
        ret[1] .= 1
        for i in range( 2, how_many ):
            ret[i] .= next_fib( how_many[i-1], how_many[i-2] )
        return ret

uint MAX_FIB = 20

fibs = calc_fibs( MAX_FIB )

for arg in argv[1:]:
    n = to_int( arg )
    if n >= MAX_FIB:
        print( "Only numbers up to %d are supported - %s is too big" % (
            MAX_FIB, n ) )
    else:
        print( "fib( %d ) = %d" % ( n, fibs[n] ) )
(fiction)
$ ./compile_time_fibonacci.pepper 4
5

Quoting

quoting.pepper
code snippet = quote:
    x + y

int x = 3
int y = 4

print( snippet.evaluate() )
(fact)
$ pepper quoting.pepper
7
quote_and_replace.pepper
# A manual implementation of template-like functionality

code sqs = quote:
    def T square_something( T inp ):
        return inp * inp

int x = 3
float y = 4.0

int   xs = sqs.replace( T=int   ).evaluate()( x )
float ys = sqs.replace( T=float ).evaluate()( y )

print( xs )
print( ys )
(fiction)
$ pepper quoting_and_replace.pepper
9
16.0

Inline C++

inline_cpp.pepper
# Will fail if this code doesn't run at runtime

int x = 1

inline_lang( "c++", """

++x;

""" )

print( x )
(fiction)
$ pepper inline_cpp.pepper
2

"Foreign" Functions

foreign_function.pepper
def_lang(python) string format_with_pyformat( string inp ):
    """
    return inp.format( lang="python" )
    """

print( format_with_pyformat( "language = {lang}" ) )
(fiction)
$ pepper foreign_function.pepper
language = python

Calculated Types

calculated_type.pepper
int config_setting = 0

def type get_number_type( int cfg ):
    if cfg > 0:
        return float
    else:
        return int

# The type of the parameter is calculated by calling a function
def bool gt3( get_number_type(config_setting) x ):
    return x > 3

print( gt3( 4 ) )
(fact)
$ pepper calculated_type.pepper
True

Web

web.pepper
import web

class HelloHandler:
    def GET( self ):
        return "Hello, world!"

# We can build either as a standalone web server (for testing, probably based
# on http://www.pion.org/projects/pion-network-library) or as a fastcgi exe
# (and possibly other ways too).
web.application( ( ".*", "HelloHandler" ) ).run()
(fiction)
$ pepper ./web.pepper
Running on http://localhost:8080 ...

Type Switch

type_switch.pepper
def my_fn( none_or(int) x ):
    type_switch x:
        case int i:
            print( i )
        case NoneType n:
            print( "It's None!" )


my_fn( make_none_or(int, 3) )

my_fn( make_none_or(int, None) )
(fiction)
$ pepper type_switch.pepper
3
It's None!

Reduce

reduce.pepper
import sys

def reduce(
        function( int, ( int, int, ) ) fn, iterable( int ) iter, int start ):
    acc = start
    for int v in iter:
        acc = fn( acc, v )
    return acc


print( reduce( lang.plus, ( 2, 4, 6 ), 0 ) )

print( reduce( lang.times, range( 1, len( sys.argv ) + 1 ), 1 )
(fiction)
$ pepper -o reduce.cpp reduce.pepper
reduce.cpp
#include <stdio.h>

int reduce( int end );

int reduce( int end )
{
    int acc = 1;
    for( int v = 1; v < end; ++v )
    {
        acc *= v;
    }
    return acc;
}

int main( int argc, char* argv[] )
{
    printf( "%d\n", 12 );
    printf( "%d\n", reduce( argc + 1 ) );

    return 0;
}
(fiction)
$ pepper reduce.pepper 1 2
12
6

Interfaces

interfaces.pepper
interface Observer:
    def void notify()

class Beeper:
    def void notify():
        print( "Beep!" )

class Blooper:
    def void do_bloop():
        print( "Bloop!" )



print( "Beeper:" )
print( Beeper.implements( Observer ) )
print( "Blooper:" )
print( Blooper.implements( Observer ) )
(fact)
$ pepper interfaces.pepper
Beeper:
True
Blooper:
False
interface_param.pepper
interface IndexableInt:
    def int __getitem__( int index )
    def int __len__()

def void print_all( implements(IndexableInt) lst ):
    for int i in range( len( lst ) ):
        print( lst[i] )

list(int) mylst = [ 3, 1, 4, 5, 9 ]

# A version of print_all taking list(int) is generated when we call it.  If
# we called it with different type arguments, another version would be
# generated.

print_all( mylst )
(fiction)
$ pepper -o interface_param.cpp interface_param.pepper
interface_param.cpp
#include <stdio.h>
#include <vector>

void print_all( std::vector<int>* lst );

void print_all( std::vector<int>* lst )
{
    for( size_t i = 0; i < lst->size(); ++i )
    {
        printf( "%d\n", (*lst)[i] );
    }
}

int main( int argc, char* argv[] )
{
    std::vector<int> mylst;
    mylst.push_back( 3 );
    mylst.push_back( 1 );
    mylst.push_back( 4 );
    mylst.push_back( 5 );
    mylst.push_back( 9 );

    print_all( &mylst );

    return 0;
}
(fiction)
$ ./interface_param.pepper
3
1
4
5
9

Debuggable versions of classes

debuggable_version.pepper
import testutils

# Imagine Widget has some horrific dependencies e.g. platform-specific
class Widget:
    def display( auto self, string text ):
        print( text ) # Does some IO or something untestable

class DoSomething:
    def void do_something( Widget widget ):
        widget.display( "Hello" )


# DoSomething.do_something can't accept any type of object except a Widget.
# How do we test it?

auto TestableDoSomething = testutils.inheritable( DoSomething )

# Now TestableDoSomething is a class like this:
# class TestableDoSomething:
#     def void do_something( implements(Widget) widget ):
#         widget.display( hs.data )

# and we can test it by supplying a fake:

class FakeWidget:
    def __init__( auto self ):
        var:
            string self.log = ""

    def display( auto self, string text ):
        self.log += text

auto fake_widget = FakeWidget.init()

auto ds = TestableDoSomething.init()

# TestableDoSomething has a do_something method that is a copy of DoSomething's
# version, but now we can pass in a fake:

ds.do_something( fake_Widget )

assert_equal( "Hello", fake_widget.log )
(fiction)
$ ./debuggable_version.pepper