-
Notifications
You must be signed in to change notification settings - Fork 0
An experimental programming language design and implementation.
License
eki/p
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
P - An Experimental Programming Language Design / Implementation
This is a programming language design I've been playing around with. I've been
calling the language 'P', but this could be short for Placeholder, as I haven't
thought of a proper name as yet.
As this is just a prototype, I've been implementing P in Ruby. The goal is to
focus on the language's syntax and the interface into the runtime libraries. I
can prototype quickly in Ruby, hence my choice.
Getting Started
---------------
You'll need Ruby. I've been using Ruby 2.0, but it would likely work fine with
Ruby 1.9.
Use git to clone the repository.
To run the repl:
> bin/p
You won't see any prompt, but you can start typing in expressions, for
example:
> bin/p
1 + 1
2
Easy!
Okay, here are a few brief examples of what we can do with P:
> bin/p
f :i (x, y) -> # P has significate whitespace, so indentation
if x + y < 10 # is important.
x * y #
else # (x, y) -> ... creates a function that takes two
10 # arguments x and y. The body continues on the
# next line (which must be indented), but the entire
(x, y) -> # function could have been written in one line.
if x + y < 10 #
x * y # f := ... binds the function to the name 'f' in
else # the current environment. Everything in P is an
10 # expression with a value, so the assignment returns
# the function which is printed by the repl (which
f( 4, 5 ) # is why you see the source for the function listed
20 # twice in this example.
f( 5, 5 ) #
10 # We invoke the function in typical fashion.
#
environment # We can inspect the current environment.
(f: (x, y) -> #
if x + y < 10 # P has two assignment operators:
x * y # := immutable assignment
else # = mutable assignment
10) #
# These only effect the binding of the string on the
foo = :bar # left-hand side to the value on the right-hand side.
"bar" # The value on the right, may or may not be immutable
# itself. Here the string 'foo' is bound to the value
environment # 'bar'. The binding is mutable.
(f: (x, y) -> #
if x + y < 10 # When we look at the environment, we can see the
x * y # mutable bindings are depicted with an '=' and the
else # immutable bindings with a ':'.
10, foo = "bar") #
# Let's see what happens when we try to change the
# bindings...
foo = 42 #
42 # In P, most objects and bindings should be immutable.
f = 42
Error: attempt to change immutable binding of f.
# Let's look at some literals...
#
42.integer? # Everything is an object, so we can ask 42 if it's
true # an integer.
#
1.5.float? # 1.5 is not a float (as it would be in many languages)
nil # (nil and false are the only false-y values in P)
#
1.5.ratio? # It turns out 1.5 is a ratio.
true #
#
1.5.numerator # All decimal literals are stored as a ratio of a
3 # numerator / denominator.
1.5.denominator #
2 #
#
1.5f.float? # If you want a floating point value, you use the
true # 'f' suffix.
#
1.5 + 1.5 # Ratios are nice, because the math is exact in a
3 # way we might expect.
#
(1.5 + 1.5).integer? # Let's look at some strings...
true #
#
:foo # The ':' prefix will create a string from a single
"foo" # identifier like string (/\w+/).
#
'foo' # Single quotes can be used to delimit a string without
"foo" # interpolation.
#
# Double quotes provide interpolation:
"hello #{1 + 1 == 2 ? :world : "friend"}"
"hello world"
# The double :: prefix will create a string to the
# end of the line (which allows for interpolation and
# any leading space is removed):
foo = :: Hello, my friend! What's your "name"?
"Hello, my friend! What's your "name"?"
# The triple ::: prefix will take a multi-line,
# idented block as a string. It allows for
# interpolation and strips the indentation spaces:
name, age = 'Max', 8
[Max, 8]
greeting = :::
Hello #{name},
I heard that today is your birthday! Can you really be #{age} years old
already? Wow, you're growing up so fast!
"Hello Max,
I heard that today is your birthday! Can you really be 8 years old
already? Wow, you're growing up so fast!"
# Let's look at some other literals...
list = [1,2,3] #
[1, 2, 3] # Here's a list. This should be pretty
# self-evident. I haven't gotten too far
list.list? # with the api for some of these data structures,
true # but they're setup to be immutable.
#
l#ist.length #
3 #
list[1] #
2 #
list.first #
1 #
list.last #
3 #
list.each( (n) -> puts( "#{n} * #{n} is #{n * n}" ) )
1 * 1 is 1
2 * 2 is 4 # each with a list will pass the value to the given
3 * 3 is 9 # function as you'd expect.
[1, 2, 3] #
# In the example, below notice that with two arguments
# the value is the second (the index is the first
# argument). This is done for consistency with maps.
list.each( (i,n) -> puts( "the value at index #{i} is #{n}" ) )
the value at index 0 is 1
the value at index 1 is 2
the value at index 2 is 3
[1, 2, 3]
# Let's look at maps (think hash table):
map = { max: 8, eric: 35 }
{max: 8, eric: 35}
map[:max] # Maps work pretty much like you'd expect, here the
8 # keys are strings. We can use the => operator if
map['eric'] # want to use any arbitrary object as the key.
35
map.each( (k,v) -> puts( "#{k} is #{v} years old" ) )
max is 8 years old
eric is 35 years old
{max: 8, eric: 35}
# As you can see, maps are also immutable and I
# haven't finished implementing a full api, yet...
map.methods
[map?, trie?, to_map, to_trie, to_hash, empty?, length, keys, values, [],
to_literal, to_string, each]
# Briefly, another data structure:
fruits = { :apple, :orange, :banana }
{apple, orange, banana}
fruits.set?
true
# Let's look at control flow...
#
x = -10 #
-10 #
#
if x < 0 # The if expression requires indentation and can
x = x * -1 # have multiple else's (or none at all).
else if x > 0 #
x -= 100 #
else #
x += 1 #
# Alternatively, if (and it's opposite: unless) can
10 # be used at the end of an expression:
x += 100 unless x > 1
10
# We also have the ternary ?: operator:
x = x < 100 ? 23 : -23
23
while x > 10 # There are also while and until loops:
x -= x / 2 #
#
6 #
x += x until x > 100
192
# As of now, there is no for loop.
g = (x: 3, y: 4) -> # Let's take another look at functions...
x + y #
#
(x: 3, y: 4) -> # This function has default values for its
x + y # parameters. These defaults can be full expressions
# and are evaluated as a part of the body of the
# function (before the rest of the body).
#
g # The function is called automatically, there is no
7 # need for empty parenthesis (like g(), for example).
#
# 'g' is bound to a closure, we can use the &
# operator to suppress automatically calling g...
#
# In this way, we can inspect the closure...
(&g).methods
[environment, function, to_string, call, parameters, arity]
(&g).call( 10, 20 ) #
30 #
#
(&g).parameters #
[x: 3, y: 4] #
(&g).parameters.first.methods
[to_string, mutable?, immutable?, optional?, required?, glob?]
(&g).parameters.first.immutable?
true
# We can pass arguments to the function in two ways:
#
g( 1, 2 ) # Positionally (x is 1, y is 2).
3 #
#
g( x: 1, y: 2 ) # Or, by name (x is 1, y is 2).
3 #
#
g( y: 1, x: 2 ) # When calling by name, we can pass the arguments in
3 # any order, or omit arguments with defaults...
#
g( y: 100 ) # Here x is the default (3) and y is 100.
103 #
# Let's look at accepting variable arguments:
g = (args: *) ->
args.each( (k,v) -> puts( "arg #{k} is #{v}" ) )
(args: *) ->
args.each( (k,v) -> puts( "arg #{k} is #{v}" ) )
g( x: 1 ) # With the splat operator, passing objects by name
arg x is 1 # results in a map. When passing by position we
{x: 1} # are given a list. Each is one consistent way of
# processing the arguments.
g( 1, 2, 'foo' )
arg 0 is 1
arg 1 is 2
arg 2 is foo
[1, 2, foo]
g( foo: 'bar', hello: 'world' )
arg foo is bar
arg hello is world
{foo: bar, hello: world}
There's more still to the language (for example, a prototypal object system),
but the above is an alright intro and much better defined than the other areas
of the language which are either rough or unimplemented.
About
An experimental programming language design and implementation.
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published