431 lines
13 KiB
Markdown
431 lines
13 KiB
Markdown
# The Pacciani programming language
|
|
|
|
|
|
Pacciani is an esoteric language largely based on the [Monicelli Programming Language](https://github.com/esseks/monicelli), which you should definitely check out!
|
|
|
|
# What's Pacciani anyway?
|
|
|
|
Pacciani is an esoterical programming language based on the so-called
|
|
"compagni di merende" from the Italian [Monster of Florence](https://en.wikipedia.org/wiki/Monster_of_Florence) trial.
|
|
|
|
|
|
# Compilation
|
|
|
|
A part of the Pacciani compiler (the lexer) is generated using `ragel`, which
|
|
you will need to have installed. If this is not the case, the configuration
|
|
script will warn you. Pacciani is developed with version 6.8, but any
|
|
sufficiently recent release should do just fine.
|
|
|
|
You will also need to have LLVM development libraries installed, version 14.
|
|
Newer versions might or might not work. CMake looks for version 14 by default, you
|
|
can override this by setting the `PACCIANI_LLVM_VERSION` variable:
|
|
|
|
$ cmake -DPACCIANI_LLVM_VERSION=15
|
|
|
|
Finally, you will need CMake, version 3.14 or higher.
|
|
|
|
A typical Makefile-based build workflow would be:
|
|
|
|
$ cd pacciani/
|
|
$ mkdir build/
|
|
$ cd build/
|
|
$ cmake .. -DCMAKE_INSTALL_PREFIX="$HOME/pcc"
|
|
$ make all install
|
|
|
|
If your tools are installed in non-standard locations
|
|
(e.g. Homebrew on Mac OS X), you can alter the search path with:
|
|
|
|
$ PATH=/path/to/ragel cmake ..
|
|
|
|
`pcc` statically links LLVM, once compiled it will only depend on the C++
|
|
runtime and on `libz`.
|
|
|
|
## Note for non-POSIX platforms (like Windows)
|
|
|
|
The external linker is called using fork+exec for simplicity. This means that
|
|
this part of the workflow will **not** work on non-POSIX systems, such as
|
|
Windows. There, you will need to disable this feature at build time. You will
|
|
only get object files (.o) that you will have to link, including a C runtime
|
|
library, by yourself.
|
|
|
|
You can disable the invocation of an external linker and make `pcc` compilable
|
|
on Windows during CMake configuration by forcing the appropriate flag to OFF:
|
|
|
|
$ cmake .. -DPACCIANI_LINKER=OFF
|
|
|
|
## Tested platforms
|
|
|
|
The reference OS for building and testing Pacciani is the most recent Ubuntu LTS.
|
|
If the build is broken there, then it's a bug. It _should_ also compile on Windows, as
|
|
well as many more POSIX systems, including Mac OS X. If you needed a patch to compile
|
|
Pacciani on your favourite platform, please send us a pull request!
|
|
|
|
# Usage
|
|
|
|
Pacciani build an executable by default on POSIX systems
|
|
(such as Linux, Mac OS X). Linking requires an external C compiler, anything
|
|
decently modern and standard-conformant should do.
|
|
|
|
A typical invocation is very similar to what you would expect from your C
|
|
compiler:
|
|
|
|
$ pcc example.pc -o example
|
|
$ ./example
|
|
|
|
Please be aware that the Pacciani compiler depends on the availability of a C
|
|
compiler and stdlib, although this dependency should be available on virtually
|
|
all platforms where you might think to run `pcc`.
|
|
|
|
# Language overview
|
|
|
|
Statements have no terminator, i.e. no semicolon `;` or the like. A single
|
|
statement can be split across multiple lines and multiple statements can be
|
|
grouped on the same line. However, keywords consisting of multiple space-separed
|
|
words **cannot** be split on multiple lines.
|
|
|
|
A comma might be inserted after each statement, if it fits the sentence ;)
|
|
|
|
Accented letters can be replaced by the non-accented letter followed by a
|
|
backtick `` ` ``, although the use of the correct Italian spelling is strongly
|
|
encouraged for maximizing the firenze effect.
|
|
|
|
## Getting started real quick
|
|
|
|
For those of you who want to get to the code ASAP, the `examples/`
|
|
folder contains a set of programs covering most of the features of the language.
|
|
|
|
|
|
## Main
|
|
|
|
The entry point of the program (the "main") is identified by the phrase:
|
|
|
|
Tutti in piedi
|
|
|
|
which marks the beginning of the _processo_ (i.e. of the program).
|
|
|
|
A value can be returned by using the the following statement:
|
|
|
|
seduti <expression>!
|
|
|
|
optionally, no value might be returned with:
|
|
|
|
seduti!
|
|
|
|
## Expressions
|
|
|
|
The usual operators are given, but spelled as words to best fit in sentences.
|
|
They are directly mapped on usual operators as follows:
|
|
|
|
| Form | Maps to |
|
|
|----------------------|---------|
|
|
| più | `+` |
|
|
| meno | `-` |
|
|
| per | `*` |
|
|
| diviso | `/` |
|
|
| maggiore di | `>` |
|
|
| minore di | `<` |
|
|
| maggiore uguale a/di | `>=` |
|
|
| minore uguale a/di | `<=` |
|
|
|
|
So `2 più 4` means `2 + 4`. `maggiore o uguale` is admitted as alternate
|
|
form of the >= operator, same for `minore o uguale`.
|
|
|
|
When evaluating binary expressions whose operands have different types,
|
|
the type of the result will be the less restrictive between the two.
|
|
This ensures that no loss takes place when evaluating an expression.
|
|
|
|
## Binary shift
|
|
|
|
Binary shift operators have a slighly different
|
|
syntax:
|
|
|
|
<what> con saluto romano a <direction> per <bits>
|
|
|
|
which is equivalent to `what >> bits` or `what << bits`, depending on the
|
|
direction, which is specified as follows:
|
|
|
|
| Phrase | Direction |
|
|
|----------|-----------|
|
|
| destra | right |
|
|
| sinistra | left |
|
|
|
|
as you might have noticed, those are simply the translation in Italian of
|
|
"left" and "right". For instance:
|
|
|
|
testimone con saluto romano a sinistra per 2
|
|
|
|
maps to `testimone << 2`.
|
|
|
|
It goes without saying, other expression can be used instead of numbers.
|
|
Also, the usual precedence rules apply.
|
|
|
|
**There is no syntax for braces in Pacciani**.
|
|
|
|
## Variables
|
|
|
|
A variable name can contain numbers, upper and lower case character and must
|
|
not start with a number (the usual rules, that's it).
|
|
|
|
A variable might be prefixed with an article to fit a sentence. The
|
|
compiler does not check concordance with the following name, but accepts any
|
|
article of the Italian language: `il`, `lo`, `la`, `i`, `gli`, `le`, `un`, `una`
|
|
`dei`, `delle`, `l'`, `un'`. For instance, `cappello` and `il cappello` refer
|
|
to the same variable.
|
|
|
|
Consequently, the articles above cannot be used as variable names.
|
|
|
|
## Assignment
|
|
|
|
A value can be assigned to a variable with the following statement:
|
|
|
|
<varname> è amico di <expression>
|
|
|
|
|
|
|
|
The `<expression>` initializer is casted to the declared type of the variable,
|
|
even if the cast will cause some loss. This feature can be (ab)used to introduce
|
|
C-style casts too.
|
|
|
|
## Declaration
|
|
|
|
Variables can be declared in any scope. There are 5 variable types, which are
|
|
directly mapped on C++/C99 types as follows:
|
|
|
|
| Type name | Mapped C type | Size |
|
|
|-----------|---------------|-------|
|
|
| Pacciani | `int` | 64bit |
|
|
| Vanni | `char` | 8bit |
|
|
| Pucci | `float` | 32bit |
|
|
| Lotti | `bool` | - |
|
|
| Canessa | `double` | 64bit |
|
|
|
|
A variable is declared with the following statement:
|
|
|
|
venga <varname>, <type>
|
|
|
|
an initialization value can be provided:
|
|
|
|
venga <varname>, <type> è amico di <expression>
|
|
|
|
for instance:
|
|
|
|
venga l'imputato, Pacciani è amico di 4
|
|
|
|
declares a variables called `testimone` of type `Pacciani` (`int`) and initializes
|
|
it to 4.
|
|
|
|
## Input/Output
|
|
|
|
Variables and expressions can be printed with the statement:
|
|
|
|
<expression> è espulso dall'aula
|
|
|
|
Conversely, a variable might be read from input using:
|
|
|
|
chiamo a testimoniare <varname>
|
|
|
|
## Loop
|
|
|
|
There is only one loop construct, equivalent to a C `do {} while();`, which is
|
|
defined as follows:
|
|
|
|
allora
|
|
<statements>
|
|
si calmi, se <condition>
|
|
|
|
For example:
|
|
|
|
venga il mostro, Pacciani è amico di 10
|
|
allora
|
|
il mostro è amico di mostro meno 1
|
|
si calmi, se il mostro è maggiore di 0
|
|
|
|
maps to:
|
|
|
|
int mostro = 10;
|
|
do {
|
|
mostro = mostro - 1;
|
|
} while (mostro > 0);
|
|
|
|
`si calmi` might be replaced by its alternate form `perfavore`
|
|
|
|
## Branch
|
|
|
|
The branch construct encompasses both the features of an `if` and a `switch`.
|
|
The best way to explain it is by comparing its various forms to the corresponding
|
|
C translation.
|
|
|
|
This is the general form:
|
|
|
|
lei conosce <variable>?
|
|
<condition>:
|
|
<statements>
|
|
o merenda <condition>:
|
|
<statements>
|
|
insomma:
|
|
<statement>
|
|
e facevate le merende insieme
|
|
|
|
where `<condition>` might be either a value or a semi-expression, that is an
|
|
operator followed by any expression. For instance:
|
|
|
|
lei conosce il testimone?
|
|
giulio:
|
|
testimone è amico di testimone meno 1
|
|
o merenda giulio diviso 2:
|
|
testimone è amico di testimone più 1
|
|
o merenda maggiore di mobiletto per due:
|
|
testimone è amico di testimone per 2
|
|
insomma:
|
|
testimone è amico di 2
|
|
e facevate le merende insieme
|
|
maps to:
|
|
|
|
if (testimone == giulio) {
|
|
testimone = testimone - 1;
|
|
} else if (testimone == (giulio / 2)) {
|
|
testimone = testimone + 1;
|
|
} else if (testimone > (mobiletto * 2)) {
|
|
testimone = testimone * 2;
|
|
} else {
|
|
testimone = 2;
|
|
}
|
|
|
|
The statement can emulate an `if () {} else {}`:
|
|
|
|
lei conosce il genio?
|
|
maggiore di mobiletto:
|
|
genio è amico di 2
|
|
insomma:
|
|
genio è amico di 0
|
|
e facevate le merende insieme
|
|
|
|
Placing multiple `o <condition>:` block is similar to a chain of `else if` in C.
|
|
|
|
The `insomma` block can be omitted:
|
|
|
|
lei conosce il genio?
|
|
maggiore di mobiletto:
|
|
genio è amico di 2
|
|
e facevate le merende insieme
|
|
|
|
Finally, here is the equivalent of a `switch () {}`:
|
|
|
|
lei conosce il genio?
|
|
1:
|
|
genio è amico di 2
|
|
o merenda 2:
|
|
genio è amico di 7
|
|
insomma:
|
|
genio è amico di 9
|
|
e facevate le merende insieme
|
|
|
|
where the `insomma` part is like the `default` block.
|
|
|
|
## Functions
|
|
|
|
|
|
## Declaration
|
|
|
|
A function is declared with the `idromassaggiatore` statement:
|
|
|
|
idromassaggiatore [<type>] <name> [con <param> <type>[, <param> <type>...]] innocente
|
|
<statements>
|
|
|
|
Where `<type>` can be omitted for a void function. For instance:
|
|
|
|
idromassaggiatore Pacciani merendivoro con mario Vanni innocente
|
|
seduti mario meno 2!
|
|
|
|
is a function of type `Pacciani`, taking one argument of type `Vanni`.
|
|
Multiple arguments must be comma-separed, like in:
|
|
|
|
idromassaggiatore Pacciani merendivoro con mario Vanni, barilotto Pacciani innocente
|
|
seduti mario meno 2!
|
|
|
|
which is a function of type `Pacciani`, taking two arguments of type `Vanni`
|
|
and `Pacciani`. It maps to:
|
|
|
|
int merendivoro(char mario, int barilotto) {
|
|
return mario - 2;
|
|
}
|
|
|
|
Finally, this:
|
|
|
|
idromassaggiatore merendivoro innocente
|
|
seduti!
|
|
|
|
is a `void` function taking no arguments and becomes:
|
|
|
|
void merendivoro() {
|
|
return;
|
|
}
|
|
|
|
Functions cannot be nested and can be declared before or after the main in any
|
|
order. `pcc` will not check that a return statement is always reachable inside
|
|
a non-void function. Failing to return a value leads to undefined behaviour.
|
|
|
|
A function might be declared with no body, in which case it's treated as a
|
|
prototype. A prototype makes the function signature known to the compiler, and
|
|
it signals that the function is implemented in another file.
|
|
|
|
## Invocation
|
|
|
|
A function is called with the `noi condividiamo` statement:
|
|
|
|
noi condividiamo <name> [con <expression>[, <expression>...] innocente
|
|
|
|
Functions might be called inside expressions. For instance, this:
|
|
|
|
il giudice è amico di noi condividiamo poeticamente con merende diviso 3 innocente per 2
|
|
|
|
maps to:
|
|
|
|
giudice = poeticamente(merende / 3) * 2;
|
|
|
|
## Exceptions
|
|
|
|
The program might be aborted immediately with the statement:
|
|
|
|
viva idduce
|
|
|
|
there are no arguments.
|
|
|
|
## Assertions
|
|
|
|
An assertion block will evaluate its expression and trigger an error message
|
|
if it is found to be 0 (logical false). An assertion is stated as:
|
|
|
|
ma cosa dice <expression>!
|
|
|
|
## Comments
|
|
|
|
Any character after `il nostro signore Gesù` is ignored until a line break is encountered. For
|
|
instance, in:
|
|
|
|
il testimone è amico di 4 il nostro signore Gesù è mio amico
|
|
|
|
`il nostro signore Gesù` is ignored.
|
|
|
|
Comments are useful to fill the "supercazzola" and make it more readable, since
|
|
any word (including reserved words) can be inserted into it.
|
|
|
|
## Meta comments
|
|
|
|
In addition to line comments, there are meta comments. A meta comment starts
|
|
with an hash sign `#` and continues until a line break is encountered, as an
|
|
ordinary comment.
|
|
|
|
They have a different graphical symbol, which can be immediately spotted inside
|
|
a long "processo". Also, ordinary comments can and should be used in an
|
|
improper way to fill the sentence, meta comments provide a mechanism for
|
|
distiguishing "real" comments.
|
|
|
|
## Reserved words and phrases
|
|
|
|
The following phrases are currently reserved with no assigned usage. They cannot
|
|
be used as variable identifiers, even if they do not serve any other purpose in
|
|
the current language revision.
|
|
|
|
* `lei la picchiava`
|