Skip to content
/ tampio Public

Tampio: An object-oriented programming language made to resemble Finnish

License

Notifications You must be signed in to change notification settings

fergusq/tampio

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

73 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The Tampio Programming Language

Tampio is a lazy purely functional programming language that looks like a natural language – Finnish. It is named after a famous Finnish programmer.

Dependencies

  • Python 3
  • Libvoikko (package libvoikko in Ubuntu and Fedora)
  • Finnish morphological dictionary (Eg. this or this. The latter may work better with newer versions of libvoikko, but the former is confirmed to work with libvoikko 3.8.)

The morphological dictionary must be unzipped to ~/.voikko/.

Usage

To open an interactive prompt:

python3 suomi.py

To evaluate a file:

python3 suomi.py file.suomi

The output will be the value of tulos.

There are several flags available, most notably --free-pure and --free-impure, which enable pure and impure free mode, respectively. The interpreter is by default in the restricted mode, which allows some optimizations but removes certain language features at the same time. The pure free mode allows all features but does not have impure optimizations. The impure free mode allows all features and all optimizations, but is impure (ie. functions do not always evaluate to same values).

Introduction

Tampio is a purely functional programming language designed to resemble written Finnish. Each program is a list of transformations that are applied to the evaluated code.

A transformation definition consists of pattern and body, separated by the on keyword. For example:

>>> kissan nimi on maija
>>> tulos on kissan nimi
>>> tulos
maija

The above program declares that kissan nimi is transformed to maija and tulos is transformed to kissan nimi. Using these rules, tulos evaluates to maija.

The patterns can contain variables, which match to all expressions.

>>> x:n nimi on maija
>>> koiran nimi
maija

Here, koiran nimi matches x:n nimi and evaluates to maija.

Syntax

Every pattern and body of a transformation is an expression.

<pattern> on <expression> [, missä <var> on <expression>]

All expressions can be inflected in different cases. Both <pattern> and <expression> must be in the nominative case.

Every transformation can have an optional missä clause, which is substituted to the pattern and the body. For example,

nolla plus luku on luku, missä luku on x:n seuraaja

is equivalent to

nolla plus x:n seuraaja on x:n seuraaja

Tampio supports two kinds of expressions: words and function calls. A word is simply a word in the source code.

There are three types of function calls: genitive calls, essive calls, binary operators. Each function call has a unique syntax.

Genitive calls have form argumentin funktio, where argumentti is the argument and funktio is the name of the function. The argument must be in the genitive case. These calls can only have one argument and have the highest precedence. When the expression is inflected, it is the name of the function that is inflected.

Essive calls can have either one or two arguments. They have form argumentti funktiona [argumentilla] where the optional last argument must be inflected in a case that is not nominative, genitive or essive. Essive calls are right-associative, meaning that eg. a f:nä b:llä g:nä means f(a,g(b)) in pseudocode. It is also possible to place the last argument before the essive word (eg. argumentti argumentilla funktiona) so it is possible to write a b:llä f:nä g:nä, which means g(f(a,b)).

There are four binary operators in addition to conjunctions: plus, ynnä, miinus and modulo. Their first operand must be in the nominative case and the second is inflected in the case of the expression.

The conjunctions ja, tai and sekä have usually the same precedence as other binary operators. They require that the first operand is in the same case as the second is. This can lead to situations where the conjunctions actually have a very high precedence:

a f:nä b:llä ja c     = ja(f(a,b),c)
a f:nä b:llä ja c:llä = f(a,ja(b,c))

Syntax cheat sheet

Precedence Name Syntax Pseudocode Inflected word Notes
1 Genitive a:n f f(a) f
1 Essive a f:nä b:llä or a b:llä f:nä f(a,b) a The case of b may vary, but must not be nominative, genitive or essive
2 Binary operator a plus b plus(a,b) b Available operators: plus, ynnä, miinus, modulo
2a Conjunctionb a ja b ja(a,b) a and b Available conjunctions: ja, sekä, tai

aThe precedence level of a conjunction depends on the cases of the operands, as both operands must share the same case.

bConjunctions are a special type of binary operator that require that the operands always are in the same case. Other binary operators require that the first operand is in the nominative case.

Evaluation

An expression is evaluated in the following steps:

  1. The expression is matches against every pattern in the current file, from up to down. The expression is substituted with the first matching expression.
  2. If no expression matches, apply steps 1 and 2 (but not step 3) to each nested expression.
  3. Repeat steps 1 and 2 until the expression and nested expressions do not match any pattern.

For example, given the following definitions, nollan seuraaja plus nollan seuraaja is evaluated as shown.

nolla plus x on x                          # def 1
x:n seuraaja plus y on x plus y:n seuraaja # def 2

nollan seuraaja plus nollan seuraaja
# (step 1) matches def 2 (x:n seuraaja plus y)
#          substitute with x plus y:n seuraaja
#          where x = nolla, y = nollan seuraaja
# (step 3) repeat
nolla plus nollan seuraajan seuraaja
# (step 1) matches def 1 (nolla plus x)
#          substitute with x
#          where x = nollan seuraajan seuraaja
# (step 3) repeat
nollan seuraajan seuraaja
# (step 1) does not match
# (step 2) no nested expression matches
# (step 3) no need to repeat, stop evaluation

Impure optimizations

The interpreter supports impure optimizations, which – while increasing evaluation speed – do unfortunately make the language impure.

For example, we can make an impure version of Fibonacci number list using the epäpuhtaasti keyword.

luvut ovat epäpuhtaasti 1 lisättynä 1:een lisättynä yhteenlaskuun sovellettuna lukujen jäseniin ja lukujen hännän jäseniin

Now, after the next Fibonacci number after 1 and 1 is calculated, the luvut list is updated to include it.

luvut ovat epäpuhtaasti 1 lisättynä 1:een lisättynä 2:een lisättynä yhteenlaskuun sovellettuna lukujen jäseniin ja lukujen hännän jäseniin

This reduces the complexity from O(2^n) to O(n). In the restricted mode, the optimizations should not cause any problems. In the impure free mode, the impurity can leak to other parts of the program and cause unintended behavior.

I/O

Tampio supports a basic IO monad that can be used to do interactive I/O. To enable the monad, execute the interpreter with the --io flag. In --io mode, the starting point of the program is not tulos, but m:n tulos, where m is a "world object".

For example, here's a simple program that prints 5:

m:n tulos on 5 tulostettuna m:ään

x tulostettuna m:ään is a function that prints x and returns a monad object. There is a bind operation liitettynä, which can be used to chain I/O actions. The following program will input two numbers and output their sum.

m:n tulos                        on luku m:stä luettuna        liitettynä ykkösvaiheeseen
x ykkösvaiheena m:lle            on luku m:stä luettuna        liitettynä kakkosvaiheeseen ja x:ään
y kakkosvaiheena m:lle ja x:lle  on summa tulostettuna m:ään,  missä summa on x plus y

kun clauses are syntax sugar for this kind of code (inspired by Haskell's do notation):

m:n tulos on summa tulostettuna m:ään, missä summa on x plus y \
    kun x on luku m:stä luettuna                               \
    kun y on luku m:stä luettuna

Examples

Lazy infinite lists

# Ones [1, 1, 1, ...]
yhdet ovat yksi lisättynä yksiin

# Natural numbers [1, 2, 3, 4, ...]
luvut ovat yhteenlasku sovellettuna yksien jäseniin ja nollan lisättynä lukuihin jäseniin

# Fibonacci sequence [1, 1, 2, 3, 5, 8, ...]
luvut ovat 1 lisättynä 1:een lisättynä yhteenlaskuun sovellettuna lukujen jäseniin ja lukujen hännän jäseniin

Finnish declension

In Finnish nouns are inflected in cases. There are a few of them, listed below. The exact meanings of the cases are not important to understand the programming language.

Case Suffix Example Notes
Nominative - kissa
Genitive -n kissan
Partitive -A kissaa
Essive -nA kissana
Translative -ksi kissaksi
Allative -lle kissalle
Adessive -llA kissalla
Ablative -lta kissalta
Illative -Vn kissaan
Inessive -ssA kissassa
Elative -stA kissasta
Abessive -ttA kissatta
Instructive -in kissoin Plural only
Comitative -ine- kissoineni Plural only, possessive suffix required

The inflection of nouns is a pretty complicated process. The exact suffix depends on vowel harmony ("A" is either "a" or "ä") and the stem of the word can change due to consonant gradation. It is outside the scope of this document to describe declension further. Inflected forms of different words can be checked from Wiktionary if needed.

License

The interpreter is licensed under the GNU General Public License, version 3 or later. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. See the LICENSE file for details.

About

Tampio: An object-oriented programming language made to resemble Finnish

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •