
Salutations
Cette semaine, j'ai entrepris le développement d'un petit interpréteur BASIC simple. Le but à terme est de pouvoir l'utiliser dans des systemes embarqués, genre PIC, AVR... pour traiter de petits scripts "clairs", et ce, sans avoir à gérer l'EMI, qui permet d'étendre la mémoire RAM du micro, sur des systèmes amovibles par exemple.
Le la syntaxe du BASIC que j'entreprends de décoder est un genre d'hybride entre le BASIC des micro d'antan, et un assembleur émulé. J'ai choisi cette voie pour des raisons pratique. En effet, si cette solution est plus stricte au niveau du script, elle est bien plus simple à décoder tout en permettant l'interprétation d'instructions très "bas niveau" (gestion de la mémoire plus poussée, registres, pile, etc...)
voilà un exemple de script :
va =0
vb=0
va = va + 1
if va <= 10
goto 3
else
va= va - 1
if va >= 1
goto 7
else
vb = vb + 1
if vb <= 10
goto 4
else
Ce petit morceau de code fait varier la valeur de va une dizaine de fois, de 1 à 10, puis de 10 à 1.
Actuellement, l'interpréteur n'en est qu'à ses balbutiement. Il découpe d'ors et déjà le script en lignes puis dresse une carte du programme et des branchements conditionnels. En effet, tout l'intéret de cet interpréteur est que le code n'est pas copié dans la RAM dans son intégralité, mais uniquement par ligne. Ensuite, en fonction de la carte dressée au préalable, il pourra exécuter telle ou telle ligne. Pour le développement, j'utilise un code qui n'ets pas le code définitif, mais une émulation de ce qui se passerait dans le PIC. Par exemple, le script ne sera pas stocké dans une variable (ca serait en contradiction avec les principes cités plus haut) mais dans un périphérique externe (genre mémoire EEPROM ou FLASH). Deplus, aucun dispositif de monitoring, tout ce qui est en rapport avec l'affichage sera viré au moment opportun.
Voilà le code actuel :
/*****************************************************************************************
BASIC interpreter
(PIC2PC - simulateur)
0.0.1
By Gery DUBIEF, www.tixlegeek.com, tixlegee AT gmail DOT com
Code à compiler exclusivement avec GCC. Non compatible WINDOWS
*****************************************************************************************
-> L'intégralité de ce code est sous license GPL. Rapportez vous au
site ci dessous pour connaître les conditions exactes de la license.
http://www.gnu.org/copyleft/
*****************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
/*****************************************************************************************
Reperes de lignes
*****************************************************************************************/
#define LBB_RCCHAR 59
#define LBB_STOPCHAR 0
/*****************************************************************************************
Index d'instruction
*****************************************************************************************/
#define LBBI_IF 100
#define LBBI_ELSE 101
#define LBBI_GOTO 102
/*****************************************************************************************
Macros du simulateur
*****************************************************************************************/
// Affiche un retour chariot
#define LBB_NRC() printf("\033[30m"); \
printf("\033[43m"); \
printf("[RC]"); \
printf("\033[37m"); \
printf("\033[40m"); \
printf("\n");
// Affiche un début de conditionnelle
#define LBB_NCS(stackind) printf("\033[33m"); \
printf("\033[40m"); \
printf("{%d\t[Debut de conditionelle IF]", stackind); \
printf("\033[37m"); \
printf("\033[40m"); \
printf("\n");
// Affiche une fin de conditionelle
#define LBB_NCE(stackind) printf("\033[33m"); \
printf("\033[40m"); \
printf("}%d\t[Fin de conditionelle IF]", stackind); \
printf("\033[37m"); \
printf("\033[40m"); \
printf("\n");
// Affiche un goto
#define LBB_NCG() printf("\033[36m"); \
printf("\033[40m"); \
printf("--> \t<GO TO>"); \
printf("\033[37m"); \
printf("\033[40m"); \
printf("\n");
// Affiche un titre
#define LBB_NTI(titre) printf("\033[30m"); \
printf("\033[47m"); \
printf("-------------------- [ %s ] --------------------", titre); \
printf("\033[37m"); \
printf("\033[40m"); \
printf("\n");
// Affiche une erreur
#define LBB_NERR(errtxt) printf("\033[31m"); \
printf("\033[40m"); \
printf("[ERROR] : %s ", errtxt); \
printf("\033[37m"); \
printf("\033[40m"); \
printf("\n");
/*****************************************************************************************
Typedefs
*****************************************************************************************/
typedef struct
{
int index; // Adresse ligne
int argstart;
int ctype; // Type
int branch; // Accord de goto
} LBB_CodeLine;
/*****************************************************************************************
Contenu de l'eeprom, normalement externe au programme.
Toutes les instructions de lectures de cette varibale devront
interragir avec le périphérique de support dirrectement.
*****************************************************************************************/
unsigned char ProgBuffer[] = "va =0;vb = 0;va = va + 1;if va <= 10;goto 3;else ;va = va - 1;if va >= 1;goto 7;else ;vb = vb + 1;if vb <= 10;goto 4;else ; ;";
int LBB_LineCursor = 0; // Curseur de lecture
int LBB_LineUltimate = 0; // Nombre de lignes
LBB_CodeLine LBB_LineIndex[1024]; // Index des Lignes
int LBB_ConditionalStack[10]; // Pile de repérage des branchements
/*****************************************************************************************
Déclaration des fonctions
*****************************************************************************************/
int LBB_strlen(char *str);
int LBB_strstr(char *str1, char *str2);
int LBB_InitConditionnal();
int LBB_Init();
/*****************************************************************************************
Fonctions
*****************************************************************************************/
/*****************************************************************************************
Découpe et cartographie les lignes de script
les ";" actuels seront transcrit comme caracteres
spéciaux (habituellement \n, codé 15) dans l'eeprom.
*****************************************************************************************/
int LBB_Init()
{
LBB_NTI("Initialisation de la trame de code");
int i=0, j=1;
while(ProgBuffer[i] != LBB_STOPCHAR)
{
if(ProgBuffer[i] == LBB_RCCHAR)
{
j++;
LBB_LineIndex[j].index = i+1;
LBB_NRC();
}
else
{
printf("%c", ProgBuffer[i]);
}
i++;
}
LBB_LineUltimate = j;
printf("\nNombre de lignes : %d \n", LBB_LineUltimate);
return(0);
}
/*****************************************************************************************
Cartographie de l'arbre conditionel
*****************************************************************************************/
int LBB_InitConditionnal()
{
LBB_NTI("Analyse des conditionelles");
int i=0, j=1, charindex = 1, LBB_ConditionalStackIndex = 0;
char LBB_InstructionBuffer[30];
for(i = 1 ; i <= LBB_LineUltimate-1 ; i++)
{
charindex = 0;
printf("Ligne n° %d : instruction ", i);
for(j=LBB_LineIndex[i].index; j<=LBB_LineIndex[i+1].index; j++)
{
if(ProgBuffer[j] == 32)
{
LBB_InstructionBuffer[charindex] = 0;
break;
}
LBB_InstructionBuffer[charindex] = ProgBuffer[j];
charindex++;
}
LBB_LineIndex[i].argstart = LBB_strlen(LBB_InstructionBuffer)+1; // Inscription de l'offset de lecture poue
// les arguments
printf("%s \t\t(offset de lecture : %d) \n",LBB_InstructionBuffer, LBB_LineIndex[i].argstart);
if(LBB_strstr(LBB_InstructionBuffer, "if")==1)
{
LBB_ConditionalStackIndex ++; // Ajout d'une adresse au STACK conditionel
LBB_ConditionalStack[LBB_ConditionalStackIndex] = i; // pour les retours
LBB_LineIndex[i].ctype = LBBI_IF;
LBB_NCS(LBB_ConditionalStackIndex);
}
if(LBB_strstr(LBB_InstructionBuffer, "else")==1)
{
LBB_LineIndex[LBB_ConditionalStack[LBB_ConditionalStackIndex]].branch = i; // Attribution de l'index actuel
LBB_NCE(LBB_ConditionalStackIndex); // au STACK précedent
LBB_ConditionalStackIndex --; //
LBB_LineIndex[i].ctype = LBBI_ELSE;
}
if(LBB_strstr(LBB_InstructionBuffer, "goto")==1)
{
LBB_LineIndex[i].ctype = LBBI_GOTO;
LBB_NCG();
}
}
if(LBB_ConditionalStackIndex > 0)
{
LBB_NERR("Arbre de branchement déséquilibré");
}
return(0);
}
/*****************************************************************************************
Renvoie la taille d'un tableau
*****************************************************************************************/
int LBB_strlen(char *str)
{
int i=0, strlen_buffer = 0;
while(str[i] != 0)
{
strlen_buffer++;
i++;
}
return strlen_buffer;
}
/*****************************************************************************************
Compare deux chaines
*****************************************************************************************/
int LBB_strstr(char *str1, char *str2)
{
int i = 0, strlen_str = LBB_strlen(str1), returned = 1;
if(strlen_str==LBB_strlen(str2))
{
while(i < strlen_str)
{
if(str1[i]!=str2[i])
{
returned = 0;
}
i++;
}
}
else
{
returned = 0;
}
return returned;
}
/*****************************************************************************************
Fonction principale
*****************************************************************************************/
int main()
{
LBB_Init();
LBB_InitConditionnal();
return 0;
}
Et, une fois compilé, ce gros sac de noeud donne ca :
Plus d'infos dans les semaines à venir.