Clem est un langage de programmation basé sur une pile minimale comportant des fonctions de première classe. Votre objectif est d'écrire un interprète pour la langue Clem. Il devrait exécuter correctement tous les exemples inclus dans l'implémentation de référence, qui est disponible ici .
- Comme d'habitude, les échappatoires standard s'appliquent.
- La plus petite entrée par nombre d'octets gagne.
Le langage Clem
Clem est un langage de programmation basé sur la pile avec des fonctions de première classe. La meilleure façon d'apprendre Clem est d'exécuter l' clem
interpréteur sans argument. Il démarrera en mode interactif, vous permettant de jouer avec les commandes disponibles. Pour exécuter les exemples de programmes, tapez clem example.clm
où exemple est le nom du programme. Ce bref didacticiel devrait suffire à vous aider à démarrer.
Il existe deux principales classes de fonctions. Fonctions atomiques et fonctions composées. Les fonctions composées sont des listes composées d'autres fonctions composées et de fonctions atomiques. Notez qu'une fonction composée ne peut pas se contenir.
Fonctions atomiques
Le premier type de fonction atomique est la constante . Une constante est simplement une valeur entière. Par exemple, -10. Lorsque l'interpréteur rencontre une constante , il la pousse vers la pile. Courez clem
maintenant. Tapez -10
à l'invite. Tu devrais voir
> -10
001: (-10)
>
La valeur 001
décrit la position de la fonction dans la pile et (-10)
est la constante que vous venez de saisir. Entrez maintenant +11
à l'invite. Tu devrais voir
> +11
002: (-10)
001: (11)
>
Notez que ce dernier (-10)
est passé à la deuxième position de la pile et (11)
occupe maintenant la première. C'est la nature d'une pile! Vous remarquerez que -
c'est également la commande de décrémentation. Chaque fois -
ou +
précédant un nombre, ils désignent le signe de ce nombre et non la commande correspondante. Toutes les autres fonctions atomiques sont des commandes . Il y en a 14 au total:
@ Rotate the top three functions on the stack
# Pop the function on top of the stack and push it twice
$ Swap the top two functions on top of the stack
% Pop the function on top of the stack and throw it away
/ Pop a compound function. Split off the first function, push what's left,
then push the first function.
. Pop two functions, concatenate them and push the result
+ Pop a function. If its a constant then increment it. Push it
- Pop a function. If its a constant then decrement it. Push it
< Get a character from STDIN and push it to the stack. Pushes -1 on EOF.
> Pop a function and print its ASCII character if its a constant
c Pop a function and print its value if its a constant
w Pop a function from the stack. Peek at the top of the stack. While it is
a non-zero constant, execute the function.
La saisie d'une commande à l'invite exécutera la commande. Tapez #
à l'invite (la commande en double). Tu devrais voir
> #
003: (-10)
002: (11)
001: (11)
>
Notez que le (11) a été dupliqué. Tapez maintenant %
à l'invite (la commande drop). Tu devrais voir
> %
002: (-10)
001: (11)
>
Pour envoyer une commande à la pile, mettez-la simplement entre parenthèses. Tapez (-)
à l'invite. Cela poussera l'opérateur de décrémentation vers la pile. Tu devrais voir
> (-)
003: (-10)
002: (11)
001: (-)
>
Fonctions composées
Vous pouvez également mettre plusieurs fonctions atomiques entre parenthèses pour former une fonction composée. Lorsque vous entrez une fonction composée à l'invite, elle est poussée dans la pile. Tapez ($+$)
à l'invite. Tu devrais voir
> ($+$)
004: (-10)
003: (11)
002: (-)
001: ($ + $)
>
Techniquement, tout sur la pile est une fonction composée. Cependant, certaines des fonctions composées de la pile consistent en une seule fonction atomique (dans ce cas, nous les considérerons comme des fonctions atomiques pour des raisons de commodité). Lors de la manipulation de fonctions composées sur la pile, la .
commande (concaténation) est souvent utile. Tapez .
maintenant. Tu devrais voir
> .
003: (-10)
002: (11)
001: (- $ + $)
>
Notez que les première et deuxième fonctions de la pile ont été concaténées et que la deuxième fonction de la pile vient en premier dans la liste résultante. Pour exécuter une fonction qui se trouve sur la pile (qu'elle soit atomique ou composée), nous devons émettre la w
commande (while). La w
commande fera apparaître la première fonction sur la pile et l'exécutera de manière répétée tant que la deuxième fonction sur la pile est une constante non nulle. Essayez de prédire ce qui se passera si nous tapons w
. Maintenant, tapez w
. Tu devrais voir
> w
002: (1)
001: (0)
>
C'est bien ce que vous attendiez? Les deux numéros assis au sommet de la pile ont été ajoutés et leur somme reste. Essayons encore. Nous allons d'abord supprimer le zéro et pousser un 10 en tapant %10
. Tu devrais voir
> %10
002: (1)
001: (10)
>
Maintenant, nous allons taper la fonction entière en une seule fois, mais nous ajouterons un supplément %
à la fin pour se débarrasser du zéro. Tapez (-$+$)w%
à l'invite. Tu devrais voir
> (-$+$)w%
001: (11)
>
(Notez que cet algorithme ne fonctionne que si la première constante de la pile est positive).
Cordes
Des cordes sont également présentes. Ils sont principalement du sucre syntaxique, mais peuvent être très utiles. Lorsque l'interpréteur rencontre une chaîne, il pousse chaque caractère du dernier au premier sur la pile. Tapez %
pour supprimer le 11 de l'exemple précédent. Maintenant, tapez 0 10 "Hi!"
à l'invite. Le 0
va insérer un terminateur NULL et le 10
va insérer un caractère de nouvelle ligne. Tu devrais voir
> 0 10 "Hi!"
005: (0)
004: (10)
003: (33)
002: (105)
001: (72)
>
Tapez (>)w
pour imprimer les caractères de la pile jusqu'à ce que nous rencontrions le terminateur NULL. Tu devrais voir
> (>)w
Hi!
001: (0)
>
Conclusions
J'espère que cela devrait suffire pour vous aider à démarrer avec l'interprète. La conception du langage doit être relativement simple. Faites-moi savoir si quelque chose n'est pas très clair :) Quelques choses ont été laissées intentionnellement vagues: les valeurs doivent être signées et au moins 16 bits, la pile doit être suffisamment grande pour exécuter tous les programmes de référence, etc. De nombreux détails n'ont pas été gravés ici parce qu'une spécification complète du langage serait prohibitive à publier (et je n'en ai pas encore écrit: P). En cas de doute, imitez l'implémentation de référence.