[JAVA] Utilisez Processing pour implémenter un environnement d'exécution pour votre propre langage de codage en direct avec un écran de prévisualisation de la console de l'éditeur

introduction

C'est celui annoncé dans la pratique EP du département FMS de l'Université Meiji le 17 septembre 2020. Eh bien, je m'en fiche, alors jetez un œil. J'ai mis le Code source sur github.

Aperçu

Le programme lui-même est implémenté en utilisant un langage appelé Processing, qui semble être une couverture de java. L'écran d'exécution se compose d'un éditeur de texte, d'une console et d'un écran d'aperçu. D4A19692-CDAF-4B48-B178-A0C1720EA227.jpeg

Grammaire de la langue

Fondamentalement, il peut être écrit dans la même syntaxe que java. Il y a certaines parties qui sont différentes de java, donc je vais vous expliquer un peu.

Déclaration de variable

La déclaration de variable a la syntaxe suivante.

let a = 10;
println(a); // -> 10
println(typeof(a)); // -> int

let b: float = 10;
println(typeof(b)); // -> float

Types intégrés actuellement disponibles

4 types et leurs arrangements. Si vous en avez envie, je veux l'augmenter bientôt. La déclaration du tableau peut être écrite comme suit.

let arr1 = {0, 1, 2};
let arr2 = {{0, 1}, {1, 2}};
let arr3 = int[2][2]; // make {{0, 0}, {0, 0}}

Aussi, il y a Rust dans un langage avec une grammaire similaire, mais dans le cas de Rust, si vous le déclarez avec let, cela devient une constante, et si vous ajoutez mut, cela devient une variable, alors que dans ce langage, let La variable est déclarée avec. Je n'ai pas encore fait de déclaration constante, mais je pense en faire une déclaration `` const '' comme JavaScript.

Vous pouvez également écrire ce qui suit.

let c, d = 10, 20;
println(c, d); // -> 10 20
c, d = d, c; // swap
println(c, d); // -> 20 10

Définition des fonctions

Les fonctions peuvent être définies avec la syntaxe suivante.

fn add(a: int, b: int) -> int {
    return a + b;
}

Supplément

Comme il est fait par beaucoup de travail urgent, il existe de nombreuses fonctions intégrées qui ne peuvent pas être prises en charge par rapport à d'autres langages, et il y a beaucoup de problèmes tels que ne pas pouvoir prendre en charge l'orientation objet en premier lieu. s'il vous plaît, pardonnez-moi...

comment utiliser

Ensuite, voyons comment ce programme fonctionne.

Génération de fenêtres

Génère une fenêtre (écran correspondant) dans l'écran d'aperçu. Cela peut être fait de la même manière que le traitement. 369C08DD-1B9F-4309-B204-637C42AAEEE9.jpeg

Dans cet exemple, l'écran avec le rapport correspondant à la fenêtre 800x600 s'affiche sur l'écran d'aperçu. Vous pouvez également spécifier la couleur d'arrière-plan en utilisant la fonction `` background () '', tout comme Processing.

Utilisation de la console

Utilisez la fonction println () '' pour imprimer une chaîne sur la console. Si vous ne souhaitez pas imprimer de saut de ligne en fin de ligne, vous pouvez également utiliser la fonction print () ''. F741BD52-53ED-4A46-BC77-8EB8A0C5A810.jpeg

La possibilité d'entrer dans la console n'a pas encore été implémentée. Pardon.

Traitement pour chaque image

Similaire au traitement, vous pouvez écrire un traitement image par image en définissant la fonction `` draw () ''. L'exemple suivant implémente le comportement consistant à changer progressivement la couleur d'arrière-plan du noir au blanc. B1C3DE40-D435-4DBB-B7AC-779F4C83A61B.jpeg C21F3A6A-0BD1-4D9A-93F4-3C7D743AD2EE.jpeg 6CA58A85-3ECC-4D18-B0F5-1C33075FC954.jpeg

Traitement pour la souris

Implémentation de la fonction à appeler lorsque la souris est cliquée si les fonctions mouseX, mouseY '' et mousePressed () '' qui indiquent les coordonnées de la souris sont définies. Ainsi, vous pouvez écrire le processus suivant pour inverser la partie cliquée et ses carrés supérieur, inférieur, gauche et droit. 083D3856-E7E3-4756-A4E1-2C5EFAABF1A8.jpeg C1B173B7-AF1C-4601-AA90-37E710CF0522.jpeg 3AE8D23F-CBA3-49ED-86BF-BA14C93DE979.jpeg

Le code est en rupture de stock, je le posterai donc séparément.

size(500, 500);

let board = int[5][5];
let count = 0;

board[0][2] = 1;
board[1][1] = 1;
board[1][2] = 1;
board[2][3] = 1;
board[2][4] = 1;
board[3][3] = 1;

fn draw() -> void {
    background(0);
    fill(255, 255, 0);
    count = 0;
    for(let i = 0; i < 5; i++) {
        for(let j = 0; j < 5; j++) {
            if(board[i][j] == 1) {
                rect(100 * i, 100 * j, 100, 100);
            }
            else {
                count++;
            }
        }
    }
    if(count == 25) {
        textSize(32);
        textAlign(CENTER);
        text("CLEAR!", width / 2, height / 2);
    }
}

fn mousePressed() -> void {
    let x = mouseX / 100;
    let y  = mouseY / 100;
    for(let i = max(0, x - 1); i <= min(4, x + 1); i++) {
        board[i][y] = 1 - board[i][y];
    }
    for(let i = max(0, y - 1); i <= min(4, y + 1); i++) {
        board[x][i] = 1 - board[x][i];
    }
    board[x][y] = 1 - board[x][y];
}

Fonctions intégrées pouvant être utilisées

Dans le programme ci-dessus, certaines fonctions que vous connaissez dans Traitement sont apparues. Certaines des fonctions disponibles dans Processing ont également été implémentées pour être utilisées dans ce langage homebrew. Les fonctions intégrées actuellement disponibles sont toutes les suivantes, avec ou sans traitement.

Implémentation interne

Passons maintenant à l'implémentation interne détaillée. À partir de ce moment, cela peut être un peu difficile pour ceux qui n'ont pas une connaissance insuffisante du traitement ou qui n'ont pas beaucoup d'expérience en programmation.

Implémentation de l'éditeur

Saisie de caractères

Il est implémenté en utilisant pleinement la fonction keyPressed '', keyPressed () '', la fonction keyTyped () '', etc. Tout d'abord, jetons un coup d'œil au code suivant dans la fonction draw () ''.

if(keyPressed) {
    if(prevKey.equals(String.valueOf(key))) {
        if(keyt < dur) keyt++;
        else {
            keyt = dur / 2;
            if(key != CODED) keyTyped();
        }
    }
    else {
        keyt = 0;
        if(key != CODED) keyTyped();
    }
}
else {
    keyt = 0;
    prevKey = "";
}

prevKey est une variable qui contient la clé précédemment entrée. keyt représente le nombre d'images écoulées depuis la saisie de la clé. dur '' est la valeur définie comme seuil pour keyt ''. Si keyt '' dépasse le seuil dur '' alors que la même clé que la dernière fois est entrée, remplacer dur / 2 '' par keyt '' et key '' Lorsqu'il ne s'agit pas d'une clé codée telle que ou Control, la fonction keyTyped () '' est appelée. Si une touche différente de la précédente est saisie, keyt n'est pas coché et 0 '' est affecté à keyt. De plus, la fonction keyPressed () '' a l'implémentation suivante.

void keyPressed() {
    if(key == CODED) {
        if(keyCode == LEFT) {
            cursor.prev();
            beforeCursor.prev();
        }
        else if(keyCode == RIGHT) {
            cursor.next();
            beforeCursor.next();
        }
        else if(keyCode == UP) {
            cursor.up();
            beforeCursor.up();
        }
        else if(keyCode == DOWN) {
            cursor.down();
            beforeCursor.down();
        }
        else {
            keyFlags.put(keyCode, true);
        }
    }
}

cursor, beforeCursor sont des objets de type Cursor et ont les méthodes line, row pour représenter les lignes et les colonnes. curseur '' représente la position actuelle du curseur. beforeCursorest utilisé pour implémenter la sélection de plage, je vais donc l'expliquer plus tard. De plus, keyFlagsest un objet de type HashMap <Integer, Boolean> '', et indique si keyCode indiquant une clé codée autre que la croix est entré. Cette fonction keyPressed () '' déplace le curseur lorsque la touche croisée est enfoncée et change les keyFlags '' lorsqu'une autre touche codée est saisie. .. De plus, pour changer les keyFlags lorsque la touche dont keyFlags est true est relâchée, la fonction `` keyReleased () '' est définie comme suit.

void keyReleased() {
    if(key == CODED) {
        if(keyFlags.get(keyCode) != null) {
            keyFlags.put(keyCode, false);
        }
    }
    keyPressed = false;
}

Le processus de changement de keyPressed '' en false '' ne devrait pas être nécessaire, mais si vous ne l'écrivez pas pour une raison quelconque, le comportement de keyPressed sera étrange, donc je l'écrirai.

La fonction `` keyTyped () '' décrit le comportement correspondant à chaque touche saisie. Si vous l'écrivez ici, ce sera long, donc si vous voulez le voir, vous voudrez peut-être le vérifier depuis Lien du code source.

Affichage de la chaîne de caractères

La chaîne saisie est stockée dans un objet de type ʻArrayList <String> `ligne par ligne. Il les affiche simplement dans l'ordre. Lorsque le nombre de lignes dépasse la plage d'affichage de l'éditeur, la barre de défilement s'affiche sur le côté droit de l'éditeur. La hauteur à laquelle la chaîne de caractères est sortie change en fonction de la position de la barre de défilement.

Implémentation des fonctionnalités

Pour vous appeler un éditeur, si vous pouvez simplement entrer des caractères, ce n'est pas différent du Bloc-notes, vous avez donc besoin de certaines fonctions. Les fonctions de l'éditeur actuellement implémenté sont les suivantes.

Lors de la mise en œuvre de la sélection de plage, l'un des beforeCursor '' et du curseur '' est le début de la plage et l'autre est la fin de la plage. Le reste est foiré dans la fonction `` keyTyped () '', donc si vous êtes intéressé, jetez un œil.

Implémentation de la console

La console stocke également la chaîne dans un objet de type ʻArrayList <String> `. L'affichage est le même que celui de l'éditeur.

Implémentation du système de traitement de la langue

Le langage implémenté cette fois est un simple langage d'interprétation typé dynamiquement. Cependant, je n'aime pas les langages typés dynamiquement, donc le code ressemble à un langage typé statiquement faisant l'inférence de type. Le flux de traitement est un peu spécial, et l'analyse de la phrase originale et l'analyse de la syntaxe sont effectuées, et l'arbre de syntaxe abstraite est interprété et exécuté, mais celui implémenté cette fois est une implémentation approximative qui omet diverses choses. Je suis. Par exemple, envisagez d'exécuter le code suivant.

let a = 10;

À ce stade, la classe `` Lexer '' produit le résultat de calcul suivant.

[
    Token("let", "let"),
    Token("id", "a"),
    Token("expr", "expr"),
    Token("int", "10"),
    Token("endExpr", "endExpr"),
    Token("endLet", "endLet")
]

C'est un objet de type «ʻArrayList ». Un objet de type Token '' a deux champs, kind '' et value ''. Token (a, b) '' est écrit comme Token '' avec la valeur de kind '' comme a '' et valeur '' comme b ''. Considérez-le comme représentant un objet type. Ensuite, ce tableau est interprété comme une séquence d'instructions et exécuté. L'exécution de l'instruction let '' se fait par le code suivant.

    .
    .
    .
else if(res.get(i).kind.equals("let")) {
    ArrayList<String> vars = new ArrayList<String>();
    ArrayList<String> values = new ArrayList<String>();
    i++;
    String type = "", exprType = "";
    if(res.get(i).kind.equals("type")) {
        type = res.get(i).value;
        i++;
    }
    while(!res.get(i).kind.equals("endLet")) {
        if(res.get(i).kind.equals("id")) {
            vars.add(res.get(i).value);
            varNames.add(loc + "$" + res.get(i).value);
        }
        else if(res.get(i).kind.equals("expr")) {
            ArrayList<Token> expr = new ArrayList<Token>();
            i++;
            int exprNum = 0;
            while(exprNum > 0 || !res.get(i).kind.equals("endExpr")) {
                if(res.get(i).kind.equals("expr")) exprNum++;
                if(res.get(i).kind.equals("endExpr")) exprNum--;
                expr.add(res.get(i));
                i++;
            }
            exprType = calc(expr, loc);
            values.add(expr.get(0).value);
        }
        i++;
    }
    int vs = values.size();
    for(int j = 0; j < vars.size(); j++) {
        if(j < vs) {
            if(type.isEmpty()) {
                variables.put(loc + "$" + vars.get(j), new Variable(exprType, vars.get(j), values.get(j)));
            }
            else {
                variables.put(loc + "$" + vars.get(j), new Variable(type, vars.get(j), values.get(j)));
            }
        }
        else {
            if(type.isEmpty()) {
                variables.put(loc + "$" + vars.get(j), new Variable(exprType, vars.get(j), values.get(vs - 1)));
            }
            else {
                variables.put(loc + "$" + vars.get(j), new Variable(type, vars.get(j), values.get(vs - 1)));
            }
        }
    }
}

res est une variable de type ʻArrayList <Token> qui reçoit le résultat du calcul de Lexer``. `` Variables '' est une variable de type `` HashMap <String, Variable> '' qui stocke des variables. `` varNames`` est une variable de type ʻArrayList qui stocke les noms des variables avec la portée à l'esprit. La gestion de l'étendue variable utilise $ ''. Plus précisément, le nom de portée de la variable ```a dans la zone globale est main $ a, et la variable définie dans la fonction func () '' dans la zone globale. Le nom considérant la portée de ```a est main $ func $ a. Puisque ʻexpr`` to ʻendExpr représente une expression, passez la colonne` `Token de ʻexpr`` à ʻendExprà la fonction calc () . Ce faisant, vous pouvez calculer la formule. De cette façon, vous pouvez exécuter l'instruction let ''.

Pour les autres instructions, la colonne Token '' est générée et exécutée de la même manière, donc reportez-vous à l'implémentation de la fonction statement () '' pour l'implémentation détaillée de chacune.

La fin

Il y a beaucoup d'autres parties qui peuvent être expliquées sur la mise en œuvre, mais la fraîcheur est la vie de l'histoire, donc je voudrais terminer l'explication ici car je souhaite publier un article le jour où ce travail est annoncé. Si vous avez des demandes, des questions, des suggestions, des suggestions, des railleries, des ridicules, etc. que vous aimeriez que nous ajoutions des commentaires, veuillez commenter cet article ou répondre directement à Mon compte Twitter. Ou envoyez un DM.

Nous nous excusons profondément pour la mise en œuvre, les explications diverses et les fins diverses. Pardon.

Si vous souhaitez obtenir une explication plus détaillée et plus polie de l'implémentation du système de traitement du langage, veuillez consulter cet article.

Recommended Posts

Utilisez Processing pour implémenter un environnement d'exécution pour votre propre langage de codage en direct avec un écran de prévisualisation de la console de l'éditeur
[Ruby on Rails 5] Utilisez WorkMail pour définir une adresse e-mail pour votre propre domaine. [Courrier professionnel]