Spécifications de Bootmonkey - Brouillon

CE QUI EST DÉCRIT CI-DESSOUS EST SUSCEPTIBLE DE CHANGER.

Dernière mise à jour du document : 28/09/2014

Tout n'est sûrement pas exhaustif. Ne pas hésiter à poser des questions. Cependant les grandes lignes du langage ne changeront pas.

Introduction

Une variable qui n'a pas été initialisée, si elle est utilisée, renverra null. Elle n'est pas différenciable des variables initialisée ayant la valeur null.

Le séparateur d'instructions est le saut de ligne. Cependant, le ; peut remplacer ce rôle, permettant ainsi de placer plusieurs instructions sur la même ligne.

Les variables ont une portée locale dans le bloc où elles sont définies. Si elle sont définies à la racine du programme, elle seront donc globales.

Les structures de contrôles sont des expressions. C'est-à-dire qu'elles retournent la dernière valeur de leur bloc et donc qu'il est possible de la récupérer dans une variable.

Les variables sont typées statiquement. Le typage est donc fixe et ne peut pas changer durant l'exécution excepté avec le type Var qui permet de typer une variable dynamiquement.

_ est un caractère spécial qui indique le vide, il permet notamment d'indiquer à une valeur qu'elle ne doit pas être capturée.

Tout est objet. C'est à dire que ce code est correct :

Num a = 5
Str b = 120.to_str() # = "120"

Types

Bool
Num
Char
Str
List<T>
Dict<V>
Var

# Partie pas claire, à rectifier
Func<(T n)*>:T
Obj
Void
Null

null est la valeur d'une variable non initialisée ou sans valeur.

Évaluations booléennes

Créez vos types

Pour évitez de multiples mots-clés pour définir des types, un seul mot-clé permet de créer des alias, des structures et des énumérations : type

# Alias
type int = Num
type ListOf<T> = List<T>
type ListOfNum = ListOf<Num>
type Add = (Num, Num -> Num)
type EmptyFunc = (-> Void)

# Structures
type Person = { String name, Num age = 18 }

# Enumérations
type Dir = { Up, Down, Left, Right }
type Color = { Red, Green, Blue, Rgb(Num, Num, Num) }
type Volume = {
 No     = 0
 Low    = 20
 Medium = 50
 High   = 80
 Max    = 100
}

Fonctions

Créer une fonction

Les fonctions retournent par défaut la dernière expression. Le mot-clé return peut être utilisé seul pour ne rien retourner (la valeur null sera renvoyée si le programme essaye de la récupérer) ou être utilisé avec une expression pour expliciter de manière précise la valeur retournée.

Num add(Num a, Num b) { a + b }

Les fonctions peuvent être surchargées. L'exemple précédent peut ainsi être surchargé ainsi afin de concaténer deux chaînes de caractères :

Str add(Str a, Str b) { a + b }

La fonction appelée dépend ainsi des types des paramètres qui y sont passés.

Les fonctions retournent la dernière expression excepté si un return est rencontré.

Les arguments de fonctions peuvent avoir des valeurs par défaut.

Void ma_fonction(Num a, Num b = 5) {
 # ...
}

Appels de fonctions

L'appel d'une fonction demande obligatoirement des parenthèses, même si elle ne possède aucun paramètre.

Num mon_num = add(2, 5) # 7
Str mon_str = add("Hello", " World!") # "Hello World!"

Fonctions variadiques

Les fonctions peuvent accepter un nombre variable de paramètres.

Num somme(Num nb, Num args...) {
 # args sera un tableau (de nombres ici) contenant les valeurs passées lors de l'appel
 # ...
}

Les fonctions à nombre de paramètres variables peuvent s'appeler de différentes manières.

Num nb

nb = somme(2) # args vaudra [] (un tableau vide)
nb = somme(2, 3, 4, 5) # args vaudra [ 3, 4, 5 ]

# il est aussi possible de faire passer plusieurs valeurs via une liste
# le ...[] sert à préciser qu'on fait passer plusieurs valeurs et non une liste
nb = sommes(2, ...[ 3, 4, 5 ]) # args vaudra [ 3, 4, 5 ]

Fonctions anonymes

Il est possible de créer des fonctions anonymes. Elles servent généralement à faire des callbacks ou des fonctions qui changent durant l'exécution du programme.

# Fonction anonyme dans une variable
# La fonction dans une variable peut être changée durant l'exécution mais pas surchargée
Func ma_func = (Num a, Num b) { a + b }

# Fonction anonyme dans une fonction
une_fonction_avec_callback((Num a, Num b) {
 # ...
})

# Fonction anonyme auto-appelée
(Num a, Num b) {
 # ...
}(3, 6)

Retourner plusieurs valeurs

Il est possible de retourner plusieurs valeur à la fin d'une fonction. En fait il s'agit de retourner une liste et de la récupérer via la syntaxe spécifique [ var1, var2, etc. ] = fonction().

une_fonction(Num a) {
 [ a + 1, a + 2, a + 3]
}

[ a, b, c ] = une_fonction(0) # a = 1 ; b = 2 ; c = 3
d = une_fonction(0) # d = [ 1, 2, 3 ]
[ e, f, g ] = d # comme a, b et c
[ b, a, c ] = [ a, b, c ] # b = 1 ; a = 2 ; c = 3

"Binding" d'une fonction

Il est possible de "binder" une fonction. _ permet d'indiquer des paramètres manquants qui seront les paramètres de la nouvelle fonction :

Num add(Num a, Num b) { a + b } # (Num, Num -> Num)
Num add5 = add.bind(_, 5) # (Num -> Num)

Num ma_var = add5(20) # = 25

Conditions

Condition if

La condition if se compose ainsi : if ... { } else if ... { } else { }.

if condition {
 # ...
} else if condition {
 # ...
} else {
 # ...
}

Le if fonctionne comme une expression et peut être assignée à une variable, c'est la dernière expression qui est retournée à la variable. Si aucune des conditions ne correspond, la variable prend la valeur du else s'il y en a un ou garde sa valeur s'il n'y en a pas.

Num a = if condition {
 5
} else if condition {
 0
} else {
 1
}

Le if profite de l'écriture raccourcie suivante :

Str a = if condition ? "ok" : "nop"

Opérateurs

<, <=, >, >=, ==, <>, and, or, not, in

Exemple de l'opérateur in qui retourne le nombre d'occurences :

List<Str> names = [ "Aymeric", "Guillaume", "Yannick" ]
Bool has_yannick = "Yannick" in names # 1

Bool o_in_helloworld = "o" in "Hello World!" # 2

Dict<Num> scores = { "Aymerick" = 200, "Guillaume" = 180, "Yannick" = 20 }
Bool has_guillaume = "Guillaume" in scores # 1

Match

Le match permet de tester plusieurs valeurs d'une variable ou expression.

match ma_var {
 "a" {
  # ...
 }
 
 "b" {
  # ...
 }
 
 else { # code par défaut, "else" peut être remplacé par "_"
  # ...
 }
}

Le match permet de tester des plages de valeurs, des valeurs multiples et des conditions :

match ma_var {
 0 .. 5 { # [0;5]
  # ...
 }
 
 6 ... 10, 50 ... 120 { # [6;10[ ou [50;120[
  # ...
 }
 
 v if v > 200 { # si ma_var est supérieur à 200
  # ...
 }
}

Le match fonctionne comme une expression et peut être assignée à une variable. Si aucune des valeurs n'est possédée par l'expression passée, la variable gardera sa valeur :

Str card = "as"

Num card_value = match card {
 "valet" { 11 }
 "dame"  { 12 }
 "roi"   { 13 }
 "as"    { 14 }
 else    { card.to_num() }
} # = 14

Boucles

Boucle simple

La boucle simple est une boucle infinie. Pour la stopper, il faut effectuer un break.

loop {
 # ...
}

Il est cependant possible de définir le nombre d'itérations ;

loop 5 {
 # 1
 # 2
}

Il est aussi possible de définir un pas :

loop 8 by 2 {
 # 0
 # 2
 # ...
}

Boucle while

La boucle while itère dans la condition est respectée.

Num i = 5
while i-- {
 # i = 4
 # i = 3
 # ...
}

Boucle do-while

La boucle do-while fonctionne de la même manière que la boucle while sauf qu'il y a une itération au minumum et que l'expression est évaluée à la fin de l'itération.

Num i = 5
do {
 # i = 5
 # i = 4
 # ...
} while i--

Boucle itérative

La boucle itérative demande une valeur initiale et une valeur finale.

for i = 0 to 5 {
 # i = 0
 # i = 1
 # ...
}

Vous pouvez aussi spécifier un pas :

for i = 0 to 12 by 2 {
 # i = 0
 # i = 2
 # ...
}

Boucle sur une liste

Parcours tous les éléments de la liste.

List<Num> ma_list = [ 2, 3, 5, 7, 11, 13 ]
for k, v in ma_list {
 # k = 0, v = 2
 # k = 1, v = 3
 # ...
}

Boucle sur un dictionnaire

Parcours tous les éléments du dictionnaire.

Dict<Str> mon_dict = { "a" = "b", "b" = "c", "c" = "d" }
for k, v in mon_dict {
 # k = "a", v = "b"
 # k = "b", v = "c"
 # ...
}

Boucle sur une chaîne

Parcours chaque caractères d'une chaîne.

Str mon_str = "Hello"
for k, v in mon_str {
 # k = 0, v = "H"
 # k = 1, v = "e"
 # ...
}

Le cas du for

Lors des boucles for, il arrive que parfois vous ne souhaitiez pas nommer de variable itérative. Cela est possible grâce au mot-clé _ qui indique de ne pas capturer la valeur dans une variable :

for _ = 0 to 20 { }

Ainsi, la boucle aura 20 itérations mais aucune variable.

Cela fonctionne aussi pour les boucles for ... in. Vous pouvez capturer, au choix, rien, la valeur, la clé ou les deux. Voici les différentes syntaxes utilisables :

# for      in my_val { } # syntaxe à réfléchir
# for _    in my_val { } # syntaxe à réfléchir
# for _, _ in my_val { } # syntaxe à réfléchir
# for v    in my_val { } # syntaxe à réfléchir
for _, v in my_val { }
for k, _ in my_val { }
for k, v in my_val { }

Classes

La création de classe fonctionne simplement. Comme la plupart des langages mais de manière simplifiée, sans visibilité ni futilités.

Le @ (this. dans les autres langages) est obligatoire pour accéder à un attribut ou appeler une méthode de la classe.

class Point {
 public new(Num x, Num y) {
  @x = x
  @y = y
 }
 
 public Void add(Num x, Num y) {
  @x += x
  @y += y
 }
 
 public Void add(Point2D p) {
  @add(p.x, p.y)
 }
}

class Point3D < Point {
 public new(Num x, Num y, Num z) {
  @parent(x, y)
  @z = z
 }
 
 public Void add(Num x, Num y, Num z) {
  @add(p.x, p.y)
  @z += z
 }
 
 public Void add(Point3D p) {
  @add(p.x, p.y, p.z)
 }
}

"Prototyper" une classe existante

Il est possible d'ajouter des méthodes à une classe native (Str, Int, etc.) ou à une classe que vous avez créé ou que vous utilisez.

@ dans une classe native représente la valeur de l'objet (vous n'avez pas accès aux valeurs internes). @ dans une classe que vous avez créé ou que vous utilisez représente l'objet et avez donc accès aux attributs.

# Prototyper une classe native
Num::negative_abs() {
 -@abs() # "@" représente ici la valeur du nombre
}

# Prototyper votre propre classe
# Les sous-classes hérite des méthodes que vous créez ici
Point::sub(Point p) {
 @x -= p.x
 @y -= p.y
}

Fonctions et attributs de classe

class Math {
 public const PI = 3.14159265359
 
 public static get_pi() {
  return @PI
 }
}

Num PI

pi = Math.PI
pi = Math.get_pi()

Listes et dictionnaires

Listes

Il y a deux méthodes pour accéder à une valeur dans une liste :

List<Num> ma_list = [ 0, 1, 2, 3, 4, 5 ]

# Récupérer la valeur de l'indice 0
# ma_list.0 # syntaxe à réfléchir
ma_list[0]
ma_list.get(0)

Dictionnaires

Il y a trois méthodes pour accéder à une valeur dans un dictionnaire :

Dict<Num> ma_list = { "foo" = "bar", "bar" = "foo" }

# Récupérer la valeur de la clé "foo"
mon_dict.foo
mon_dict["foo"]
mon_dict.get("foo")

Réflexions

Aucune réflexion.

Idées

Listes de compréhension

List<Num> a = [ 0 loop 5 ]
# a = [ 0, 0, 0, 0, 0 ]

List<Num> b = [ i for i from 0 to 4 ]
# b = [ 0, 1, 2, 3, 4 ]

List<Num> c = [ i for i from 0 to 4 if i % 2 == 0 ]
# c = [ 0, 2, 4 ]

Dict<Num> ages  = { "Robert" = 34, "Léa" = 26, "Jean" = 55 }
List<Str> names = [ name for name, _ in ages ]
# names = [ "Robert", "Léa", "Jean" ]

Exemples

Cas fictif du jeu "Plus ou moins"

pack fc.examples.plus_ou_moins

use stdio as io
use math

Num nb  = math.rand(1, 100)
Num cpt = 0

Num val_test

do {
 val_test = io.ask("Saisissez un nombre entre 1 et 100 : ").to_num()
 cpt++
 
 if val_test > nb {
  io.writeln("C'est moins")
 } else if val_test < nb {
  io.writeln("C'est plus")
 }
} while val_test <> nb

io.writeln("Bravo, tu as gagné en ${cpt} coups !")

Exemple qui présente un peu tout

Num me = 38

Dict<Num> friends = {
 "Robert"  = 32,
 "Paul"    = 54,
 "Jean"    = 29,
 "Jacques" = 42
}

List<Num> ages = [ age for _, age in friends ]
# [ 32, 54, 29, 42 ]

ages.sort((Num a, Num b) { a - b })
# [ 29, 32, 42, 54 ]

Bazar

# Syntaxe de Bootmonkey (BM)
# Par Pif, booti386, Gorgio et SuperMonkey
# Dernière mise à jour : 31/07/2014

enum Volume {
  LOW    = 20
  MEDIUM = 50
  HIGH   = 100
}

interface Animal {
  Void
}

# S'inspirer de :
# - http://haxe.org/manual/lf-pattern-matching.html
# - http://fr.wikibooks.org/wiki/OCaml/Structures#Filtrage_par_motifs
switch value { when < 0 { } when < 5 { } when >= 5 { } }
when ::= < 0 (inférieur) | <> 20 (différent) | 0 or 5 (ou) | 0 to 5 (entre (bornes includes))

# Déclarer un type
"type" nom_type = type

exemple :

type Player = {
  Str name,
  { Num x, Num y } position
}

# Déclarer une variable
type nom_variable = valeur

# Déclarer une constante
"const" [type] nom_variable [ = valeur ]

# Déclarer une fonction
type nom_fonction(arguments) { }

# Déclarer une classe
[final] class NomClasse [ < NomClasseParente ] { }

# Déclarer un attribut
[public|protected|private] [static|const] type nom_attribut [ = valeur ]

# Déclarer une méthode
[public|protected|private] [static] [final] type nom_methode(arguments) { }

# Suite de fibonacci
Num fibo(Num n) {
  if n <= 1 {
    n
  } else {
    fibo(n - 1) + fibo(n - 2)
  }
}
class Player {
 String name
 List<Card> cards
 
 new (String name) {
  this.name = name
  cards = []
 }
 
 Void add_card(Card c) {
  cards.push(c)
 }
}

class Card {
 Int value
 String type
 String name
 
 new(Int value, String type) {
  this.value = value
  this.type = type
  
  name = "$value de $type"
 }
 
 Num get_value() {
  match value {
   "valet" { 11 }
   "dame"  { 12 }
   "roi"   { 13 }
   "as"    { 14 }
   else    { value.to_num() }
  }
 }
}

class Game {
 List<Card> cards
 List<Player> players
 
 new() {
  List<Var> value_cards = [ 2, 3, 4, 5, 6, 7, 8, 9, 10, "valet", "dame", "roi", "as" ]
  List<String> types_cards = [ "coeur", "pique", "trèfle", "carreau" ]
  
  for _, value in value_cards {
   for _, type in type_cards {
    add_card(value, type)
   }
  }
  
  players = []
 }
 
 Void add_card(Var value, String type) {
  cards.push(new Card(value, type))
 }
 
 Void add_player(String player_name) {
  players.push(new Player(player_name))
 }
}

Game game = new Game()
game.add_player("Yannick")
game.add_player("Guillaume")

Notes

Packages

Voici les portés des variables, fonctions, classes, interfaces et énumérations dans les packages :

Références

Entrée du programme

L'entrée du programme s'effectue dans une fonction nommée main que vous pouvez placer où vous voulez :

Num main(List<Str> args) {
 # votre code
 
 return 0
}

Metadata

@nom_meta(cle=value)