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.
Réponses:
Haskell,
931921875ce n'est pas encore complètement joué mais ce ne sera probablement jamais le cas. Pourtant, il est déjà plus court que toutes les autres solutions.
Je jouerai au golf plus bientôt.Je n'ai pas envie de jouer au golf plus que ça.a probablement quelques bugs subtils car je n'ai pas joué avec l'implémentation de référence C.
cette solution utilise le type
StateT [String] IO ()
pour stocker un programme clem "exécutable". la plupart du programme est un analyseur qui analyse le "programme exécutable".afin d'exécuter cette utilisation
r "<insert clem program here>"
.la source
Python,
16841281 caractèresVous avez fait tout le golf de base. Il exécute tous les exemples de programmes et correspond à la sortie caractère par caractère.
Test :
Rassemblez clemint.py , clemtest_data.py , clemtest.py et un
clem
binaire compilé dans un répertoire et exécutezclemtest.py
.Expansion :
La version la plus non golfée est celle-ci . Suivez avec celui-là.
S
est la pile principale. Chaque élément de la pile est une liste de 3, parmi:Pour les constantes,
f
est une fonction qui pousse la constante sur la pile. Pour l'atmosphère,f
est une fonction qui exécute l'une des opérations (par exemple-
,+
). Pour les composés,fs
est une liste d'éléments.xec
exécute un élément. S'il s'agit d'une constante ou d'un atomique, il exécute simplement la fonction. S'il s'agit d'un composé, s'il n'y a pas encore eu de récursivité, il exécute chaque fonction. Ainsi , l' exécution(10 20 - 30)
exécutera chacune des fonctions10
,20
,-
et30
, laissant10 19 30
sur la pile. S'il y a eu récursivité, il pousse simplement la fonction composée sur la pile. Par exemple, lors de l'exécution(10 20 (3 4) 30)
, le résultat devrait être10 20 (3 4) 30
non10 20 3 4 30
.L'imbrication était un peu délicate. Que faites-vous en lisant
(1 (2 (3 4)))
? La solution est d'avoir une pile de piles. À chaque niveau d'imbrication, une nouvelle pile est poussée sur la pile de piles, et toutes les opérations de poussée vont sur cette pile. De plus, s'il y a eu imbrication, les fonctions atomiques sont poussées au lieu d'être exécutées. Donc, si vous voyez10 20 (- 30) 40
,10
est poussé, alors20
, alors une nouvelle pile est créée,-
et30
est poussée sur la nouvelle pile, et)
saute la nouvelle pile, la transforme en élément et la pousse sur la pile un niveau plus bas.endnest()
poignées)
. C'était un peu délicat car il y a un cas spécial où un seul élément a été poussé et nous repoussons sur la pile principale. Autrement dit,(10)
devrait pousser la constante10
, pas un composite avec une constante, car alors-
et+
ne fonctionne pas. Je ne sais pas si c'est fondé sur des principes, mais c'est la façon dont cela fonctionne ...Mon interprète est un processeur caractère par caractère - il ne crée pas de jetons - donc les nombres, les chaînes et les commentaires étaient quelque peu ennuyeux à gérer. Il y a une pile distincte
N
, pour un numéro en cours de traitement, et chaque fois qu'un caractère qui n'est pas un numéro est traité, je dois appelerendnum()
pour voir si je dois d'abord compléter ce numéro et le mettre sur la pile. Que nous soyons dans une chaîne ou un commentaire est gardé par des variables booléennes; quand une chaîne est fermée, elle pousse tous les entrailles de la pile. Les nombres négatifs ont également nécessité une manipulation spéciale.C'est à peu près tout pour l'aperçu. Le reste a mis en œuvre tous les Encastrements et en veillant à faire des copies en profondeur
+
,-
et#
.la source
C 837
Merci à @ceilingcat d'avoir trouvé une version bien meilleure (et plus courte)
Cela traite tout comme de simples chaînes - tous les éléments de la pile sont des chaînes, même les constantes sont des chaînes.
Essayez-le en ligne!
Une version moins golfée de mon original (contrairement à la version golfée, celle-ci imprime la pile quand elle se termine si elle n'est pas vide et prend un paramètre -e pour que vous puissiez spécifier le script sur la ligne de commande au lieu de lire dans un fichier):
la source