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