QScript, encore un langage de programmation expérimental

a marqué ce sujet comme résolu.

Bonjour à tous,

N’en déplaise à tous ceux qui diront « encore un », je commence à avoir un truc un minimum potable alors je me suis dit que c’était le moment de commencer à partager pour avoir des avis ou idées extérieures. J’ai pas d’idée de nom (je suis nul pour ça), du coup j’ai appelé mon langage QScript. Il est suceptible de changer de nom encore plusieurs fois (d’autant plus que QScript est sûrement déjà pris), mais pour le moment c’est pas très important.

Voici donc « encore » un mini-langage expérimental. IL est plus si mini que ça mais enfin bon…

Pour les gens pressés, le lien de téléchargement c’est en bas du post, mais je vous conseille quand même fortement de lire la suite sinon vous serez sûrement déçu, je ne prétends pas révolutionner le monde.

Concept et inspirations

On peut dire que mon mini-langage est une sorte de croisement entre JavaScript, Python et Ruby. Tout ça en même temps et en mieux évidemment. En bref le typage est faible et dynamique, bien qu’on déclare explicitement des classes comme en Java.

Mon langage est implémenté en C++14. Pour l’implémentation, je me suis pas mal inspiré de Wren 1, et un peu de lua 2.

Le parsing est fait à l’ancienne sans bibliothèque externe. Le code source est lu, transformé en structure arborescente, et le bytecode est généré ensuite à partir de là. Je ne sais pas si on peut vraiment parler d’AST par contre. J’essaie dans la mesure du possible que la syntaxe reste à la fois agréable, souple et facile à parser. J’aurais bien aimé me débarrasser du signe $ parce que c’est fondamentalement moche, mais je n’ai pas trouvé comment faire autrement sans compliquer énormément le parsing… c’est mon seul gros regret au niveau de la syntaxe. Sinon elle est assez classique, on pourrait dire que c’est du python sans obligation d’indenter, ou du JavaScript moins quelques parenthèses superflues. IL y a quelques extraits de qScript plus bas dans ce post qui vous donneront déjà une petite idée.

La VM fonctionne sur base d’une pile. Sur la pile d’un fil d’exécution, on place les variables locales, puis les valeurs temporaires utilisées pour les calculs. Quand on appelle une fonction, on décale la base de la pile et quand on la quitte, on restaure la base à son état précédent. Je me suis beaucoup inspiré de ce que faisait Wren pour la VM. Celle de lua est beaucoup plus compliquée (encore trop pour moi ) car c’est une machine à registres.

Coup d’oeil sur la syntaxe

Ca serait un peu long de tout détailler ici, je me contenterai d’un exemple commenté qui passe en revue un peu tout. Pour plus d’informations, vous pouvez lire [language-syntax.txt] qui explique tout: types de base, structures de contrôle, fonctions, programmation orientée objet, etc.

# Un exemple de classe
class Vector {
# Constructeur avec 3 paramètres comprenant des valeurs par défaut
constructor (x=0, y=0, z=0) {
_x = x # On définit implicitement un champ x
_y = y # on sait qu'on fait référence à un champ grâce au `_`
_z = z
}

# ON définit des accesseurs pour nos trois champs x, y et z
# Si la dernière instruction d'une méthode est une expression, alors le return est implicite
x { _x }
y { _y }
z { _z }

# On définit des mutateurs
x= (val) { _x=val }
y= (val) { _y=val }
z= (val) { _z = val }

# ON définit une méthode ordinaire
# Comme elle ne prend aucun paramètre, on peut omettre les parenthèses lors de son appel
# i.e. myvector.length et myvector.length() sont identiques
length { sqrt(_x**2 + _y**2 + _z**2) }

# Surcharge d'opérateur
# On n'a pas accès aux champs d'un autre objet (même s'il est censé être de la même classe), i.e. other._x est invalide; on doit passer par les accesseurs.
+ (other) { Vector(x+other.x, y+other.y, z+other.z) }
}

let vector1 = Vector(1, 2, 3)
let vector2 = Vector(3, 4)
let vector3 = vector1 + vector2 # ON appelle le + surchargé
print(vector2.length) #5
print(vector3.x) #4
print(vector3.y) #6
print(vector3.z) #3

# Un peu de listes et autres conteneurs
let tuple = ((1, "one"), (2, "two"), (3, "three"), (4, "four"))
let set = <1, 2, 3, 4, 5> 
let list = [x**2 for x in set] # [1, 4, 9, 16, 25]
list.sort($(a,b): a%10<b%10) # [1, 4, 25, 16, 9]
list.sort(::>) # [25, 16, 9, 4, 1]

# Exemple de fonctions/lambda/closure bref appelez-les comme vous voulez
let g = $(start) {
return $(x) {
start+=1  # La variable start est prise dans la fermeture
return start*x
}}
let f1 = g(3), f2 = g(7)
print(f1(5)) #20
print(f1(6)) #30
print(f2(8)) #64
print(f2(10)) #90

# Exemple avec les fils d'exécution, un genre de générateur
let fibonacci = $*{
let a=0, b=1
while true {
let tmp=a
a+=b
b=tmp
yield a
}}

for var i in 0..10 {
# successivement 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
print(fibonacci()) 
}

# Encore des trucs avec les méthodes
let modulo = Num::% # En gros c'est la même chose qu'écrire $(a,b): a%b
print(modulo(10, 4)) #2

let tripler = 3::* # C'est un raccourci pour $x: 3*x
print(tripler(12)) #36

# Et ça, ça sert vraiment à rien, mais c'est rigolo
let a = 3, b = 4
Num::* = Num::/
print(a*b) #0.75 (!)

Performances

Je trouve que les benchmarks ne servent pas à grand chose, mais pour ceux que ça amuse, voici:

from time import clock
from math import sin
t = clock()
s = 0
for i in range(0, 10000000):
  s+=sin(i)
t = clock() -t
print(t)
let t = clock()
let s = 0
for i in 0..10000000: s+=sin(i)
t = clock() -t
print(t)

Résultat chez moi: Python 2.71 vs. QScript 2.29. Voilà, je vous ai prévenu que c’était totalement inutile car absolument pas représentatif de scripts réels. D’autant plus que python est connu pour être lent sur les boucles.

Histoire et motivations

Mon but initial était d’ajouter du scripting pour un jeu. Comme tout le monde j’ai commencé par embarquer lua. C’est éprouvé, c’est ultra-rapide. Sauf que:

  • Les tableaux commencent à 1, c’est une fonctionalité contre-intuitive qui n’apporte absolument aucun bénéfice
  • C’est un langage objet orienté prototype, et c’est aussi très troublant (et vite compliqué) quand on n’a pas le mot-clé class
  • Lua ne supporte pas l’UTF-8 (ou du moins n’apporte pas vraiment d’aide pour le gérer nativement), et j’y tiens quand même, parce que mon jeu est multilingue
  • C’est assez personnel, mais je n’aime que moyennement la syntaxe; j’ai toujours largement préféré les langages à accolades plutôt que les langages à begin…end

Ayant atteint les limites de lua, j’ai tenté d’embarquer python 4. Python est un langage facile, populaire, et la bibliothèque stnandard est très fournie. L’embarquer dans un programme C++ est beaucoup plus complexe que lua, mais ça reste encore très jouable. Sauf que:

  • Embarquer python c’est quand même assez gros, de l’ordre de 30 Mo au moins, même si on supprime tous les modules non indispensables. Non je ne m’en fiche pas royalement comme la plupart des gens, non mon jeu ne fait pas 10 Go, non je n’ai pas moi-même la fibre, et oui il y a des joueurs en Afrique qui ont des petites connexions (et ça j’ai des preuves).
  • Pour l’embarquer vraiment bien, il faudrait recompiler python soi-même, ce que j’ai vite abandonné car c’est long et hyper compliqué. J’avais pris le python36.dll inclus dans la distribution standard pour windows, mais j’ai eu des conflits de MSVCRT (mon programme et le runtime python étaient linkées à des versions différentes de la MSVCRT et je n’ai pas pu tout résoudre)
  • Mais surtout on a un énorme problème de sécurité, en cela qu’on ne peut pas totalement empêcher le scripteur d’utiliser la fonction open ou d’importer le module os car ils sont beaucoup trop implantés dans le coeur de python; il va sans dire que c’est un point critique pour un jeu, un utilisateur mal intentionné pouvant faire des dégâts chez le joueur innocent…

Après python j’ai pensé à JavaScript. Je trouve que JavaScript est devenu un chouette langage depuis ES6, ES2015 et ce qui a suivi. Ce qui est fourni en standard dans le langage suffit largement pour scripter un jeu. D’ailleurs sauf erreur unity propose JavaScript comme langage de script; malheureusement je n’utilise pas unity et je ne vais pas l’utiliser. De base si on prend vraiment V8 et pas Node.js, il n’y a pas de sockets, pas d’accès aux fichiers, etc. donc c’est totalement sûr de ce point de vue.

le code C++ d’exemple a l’air vraiment cool et pas si compliqué à embarquer. Sauf que… après avoir télécharger 4 Go de bouzin, je ne comprends toujours pas comment compiler leur m… C’est quoi ce délire avec perl, python 2.7, et des build tools qui sont nécessaires pour compiler d’autres build tools ? J’abandonne rapidement, avec l’intime conviction que Google a fait exprès d’obfusquer et compliquer son truc… oui monsieur, vous voyez, c’est libre… mais en fait c’est proprement incompréhensible et donc totalement inexploitable sauf à vous appeler Google vous-mêmes.

Toujours pour JavaScript, j’ai aussi évidemment trouvé SpeederMonkey et TraceMonkey. Mais là encore c’est compliqué. L’un des deux est considéré comme obsolète mais je n’ai pas vraiment compris lequel; j’ai pas trouvé beaucoup de doc non plus, ou alors ça avait l’air d’être un peu le foutoir sur la MDN. Et pourquoi est-ce qu’ils implémentent toujours ça en C ? Je croyais que firefox était écrit en C++ ? J’aimerais bien un truc en C++ quand même.

Du coup j’ai essayé de revenir à quelque chose de plus soft et plus spécialisé. J’ai découvert AngelScript 3, qui est ma foi plutôt bien foutu, et semble-til un temps soi peu déjà utilisé dans quelques projets. J’ai bossé un bon moment avec, j’ai même soumis un ou deux bugs à son auteur, ils ont été corrigés.

Là le problème que j’ai quand même trouvé, c’est que le concepteur laisse faire trop de choses soi-même. Les include, et même les strings et les tableaux sont à implémenter soi-même, et j’ai trouvé que les implémentations proposées en exemple étaient plutôt bof. Bien que tout soit parfaitement prévu, la magouille pour avoir un système d’include et séparer son code dans plusieurs fichiers, c’est vraiment du gros bricolage. Pour supporter les commentaires il faut même faire une fonction qui remplace ce qu’il y a entre /* et */ avec des espaces. La bonne blague.

Ah, et puis finalement, un langage statiquement typé, c’est peut-être pas assez dynamique, pas assez flexible pour faire du scripting comme on l’entend habituellement ? En outre, plus j’utilise le langage, plus je sens qu’il y a un problème avec les handles. Je les trouve de plus en plus incohérents. Des fois on doit les utiliser, des fois on ne doit pas, des fois on ne peut pas; des fois ça se comporte comme en Java, des fois ça se comporte comme en C++. En plus des handles il existe toujours les références à la C++. Ca me parait totalement illogique d’avoir recours à un constructeur de copie ou un opérateur d’affectation dans certains cas alors qu’on travaille avec des handles ou des tableaux de handles… Au final on s’y perd.

Plus tard j’ai découvert Wren 1. IL est assez sympa ce petit langage; d’ailleurs je m’en suis beaucoup inspiré pour créer le mien. Je l’ai sérieusement considéré pendant un temps pour remplacer AngelScript, au point de le forker pour essayer des choses avec. Mais il y a malgré tout certaines choses qui me dérangent:

  • C’est encore et toujours du C et rien que du C. En 2018 je trouve que c’est quand même dommage !
  • Le code est compilé en une seule passe qui fait tout en même temps et rien n’est prévu pour enregistrer du bytecode. En clair ça signifie que les scripteurs qui publient leur travail sont forcément obligés de publier leur code source… pas forcément cool, tout le monde n’adhère pas à l’open source forcé; et en plus ça aide les tricheurs et pirates potentiels. Je n’ai pas encore implémenté l’enregistrement en bytecode moi-même mais je pense bien y arriver.
  • La façon d’embarquer du code C natif est un peu étrange: on doit déclarer la classe et les méthodes en Wren, et retourner les fonctions dans des callbacks; c’est moyennement pratique pour mettre à disposition un grand nombre d’objets C/C++ en Wren. Vous verrez que de mon côté, je me suis bien amusé avec les templates pour faire en sorte que la mise à disposition d’API C++ en QScript soit assez facile.
  • ET le plus gros problème, la réentrance n’est pas officiellement implémentée. En clair ça signifie qu’une méthode Wren implémentée en C ne peut pas elle-même appeler une méthode Wren par derrière. Par exemple, impossible d’implémenter une fonction de tri de liste en C qui ferait appel à une fonction de comparaison implémentée en Wren, ce qui oblige à refaire un algorithme de tri 100% en Wren. A ce stade je me dis que j’ai autre chose à faire que réinventer quicksort et que copier du code de quelqu’un d’autre en mode j’emplile les couches de scotch.

Alors voilà. Après ces quelques expériences plus ou moins mitigées, je me suis mis en tête que j’allais m’y essayer: concevoir mon petit langage de scripting à moi.

Au niveau des performances et de la simplicité d’utilisation, je suis probablement complètement à l’ouest, mais ça ne fait rien. Même si je ne parviens pas à mon but, j’aurai malgré tout appris énormément de choses sur les coulisses des langages qu’on utilise tous les jours; quoi qu’il arrive c’est une réussite. Au passage j’ai aussi appris bien des choses sur C++ lui-même parmi les plus avancées: utilisation des templates, les vtables, le RTTI, …

Merci de m’avoir lu jusqu’au bout.

Téléchargement

J’ai évidemment mis ça sur github, mais avant que vous n’alliez voir je préfère vous prévenir, le code n’est pas très propre ni très commenté. Ca fait partie des choses que je dois encore améliorer. Lien github: https://github.com/qtnc/qscript

Pour compiler vous-mêmes, vous aurez besoin de quelques-unes des bibliothèques de boost dont boost::regex, et utf8cpp. J’utilise boost::regex parce que std::regex n’existe visiblement pas chez moi avec MinGW, tout comme std::mutex, std::thread et d’autres par ailleurs.

Si vous ne voulez pas vous casser la tête à compiler vous-mêmes, vous trouverez un zip pour windows ici: http://vrac.quentinc.net/QScript.zip
Fonctionne en 32 et 64 bits. Je m’excuse d’avance auprès des linuxiens et des maqueux.

+5 -0

Donc … rien à voir avec Qt ? xP

Déjà bravo pour avoir quelque chose de fonctionnel, et pour l’effort que tu fais sur la doc, continue comme ça, courage :)

J’ai pas bien saisi, sûrement par méconnaissance et incompréhension, tu dis qu’il s’agit d’un langage dynamiquement et faiblement typé, je pensais donc qu’on peut assigné des champs après la déclaration de la structure de donnée comme en JS ou en Lua

class vector{
constructor(_x=0,_y=0,_z=0);
var _x;
var _y;
var _z;
}

let vector1 = vector(1,2,3)
vector1.w = 0
vector1.print = $(self){
print(self.w)
print(self.x)
print(self.y)
print(self.z)
}
vector1.print()

Est-ce que c’est (ou ce sera) le cas ? Je n’en ai pas vu dans tes exemples

Et autre question, aucun de tes bouts de code n’est indenté, l’indentation est-elle interdite/non supportée ?

+0 -0

Donc … rien à voir avec Qt ? xP

Non, absolument rien à voir. D’ailleurs je préfère WXWidgets.

Déjà bravo pour avoir quelque chose de fonctionnel, et pour l’effort que tu fais sur la doc, continue comme ça, courage

Merci.

J’ai pas bien saisi, sûrement par méconnaissance et incompréhension, tu dis qu’il s’agit d’un langage dynamiquement et faiblement typé, je pensais donc qu’on peut assigné des champs après la déclaration de la structure de donnée comme en JS ou en Lua.

En fait mon concept est un peu battard. On ne peut pas rajouter des champs, mais par contre on peut rajouter des méthodes.

vector1.w = 0
Ca ça produira une erreur, la classe n’a pas de méthode w=

vector1.print = $(self){ ... }
Ca ça produira aussi une erreur, car ça tentera d’appeler la méthode print= avec la lambda en paramètre.

Par contre ceci fait ce que tu attends, ajouter une méthode print à la classe Vector:
Vector::print = $(self){ ... }

Est-ce que c’est (ou ce sera) le cas ? Je n’en ai pas vu dans tes exemples

Non ça ne sera jamais le cas; ou tout au moins je ne crois pas que ce soit possible de le faire. Il y a deux raisons qui m’empêchent d’ajouter des champs à une classe après qu’elle ait été définie:

  • Une fois la compilation terminée, tout comme pour les variables locales, j’oublie totalement le nom des champs. Dans le bytecode ce ne sont que des index.
  • La taille en mémoire occupée par une instance est fixée à la définition de la classe. En d’autres termes quand je rencontre le }, je sais combien j’ai découvert de champs _x. Pour Vector en l’occurence il y en a trois, donc si on parle en terme de C++, sizeov(Vector)==sizeof(base)+3*sizeof(uint64_t). Techniquement je pourrais changer le nombre de champs après coup et tous les nouveaux objets auraient un champ de plus, mais ça veut dire que ce n’est pas rétroactif sur les objets déjà instanciés; c’est là tout le problème.

Ca fonctionne avec les méthodes parce que contrairement aux champs, je conserve les noms après la compilation. D’abord parce que sinon je ne crois pas que je pourrais coder la fonction import, et ensuite parce que sinon je n’imagine pas la difficulté du débogage. Dans le bytecode, cependant, les méthodes aussi n’ont que des index.

C’est un choix. En réalité je suis donc seulement à moitié dynamique. Si j’avais voulu être totalement dynamique, il aurait fallu que les instances ne soient que de bêtes hashtables. C’est le principe de base de lua avec ses tables. C’est aussi à peu près ce que fait python car dans chaque PyObject il y a un champ __dict qui n’est autre qu’un dictionnaire.

Un des avantages de faire comme ça, c’est la vitesse; c’est toujours plus rapide de travailler avec des index plutôt qu’avec des strings. L’autre avantage c’est que le bytecode ne possède ni d’instruction avec des paramètres de taille variable, ni n’est bourré de constantes string pour chaque variable locale et chaque méthode déclarée ou appelée. Au final il reste très peu de strings manipulées directement par la VM, on pourrait presque imaginer retirer totalement le type String du système si vraiment on n’a pas besoin de manipuler de chaînes de caractères.

Et autre question, aucun de tes bouts de code n’est indenté, l’indentation est-elle interdite/non supportée ?

Les sauts de ligne ont une importance, mais pas les espaces et les tabs; l’indentation est donc parfaitement supportée et n’a aucune influence. Cela dit, je n’ai pas vraiment testé…

Si je n’indente pas naturellement quand je n’y suis pas obligé (c’est-à-dire quand je ne suis pas au boulot et quand je ne fais pas du python), c’est pour des raisons beaucoup plus profondes. Certains anciens et/ou modérateurs doivent sûrement savoir pourquoi. Si vous tenez vous aussi vraiment à le savoir, faites des recherches. Je ne le cache pas et je n’ai pas à le cacher, mais je ne pense pas pour autant que ce soit nécessaire dd’exposer la raison ici. Ca détournerait le sujet vers d’autres questions ô combien importantes et intéressantes, mais hors sujet dans le cas présent.

+0 -0

Donc … rien à voir avec Qt ? xP

Non, absolument rien à voir. D’ailleurs je préfère WXWidgets.

QuentinC

Haha, je dis ça juste parce que c’est de suite ce à quoi me fait penser un nom en "QCamelCase"
( pas trop touché à WxWidget mais ses premiers codes d’exemple en getting started ne m’attirent pas du tout avec toutes ces macros :o )

Par contre ceci fait ce que tu attends, ajouter une méthode print à la classe Vector:
Vector::print = $(self){ ... }

QuentinC

à vrai dire, c’est pas tout à fait ce que j’attendais, puisque ce que je suggérais c’est qu’à l’instar de JS et Lua, la méthode est ajoutée uniquement à cette instance, pas à la classe

Mais très bien, j’ai la réponse à ma question :)

  • Une fois la compilation terminée, tout comme pour les variables locales, j’oublie totalement le nom des champs. Dans le bytecode ce ne sont que des index.
QuentinC

J’avais pas compris qu’on devait forcément le compiler, je croyais que c’était optionnel pour optimiser et obfusquer mais qu’on pouvait aussi exécuter directement le script

C’est un choix.

QuentinC

C’est sûr, et ça a ses avantages comme tu l’as bien exposé :)

Si je n’indente pas naturellement quand je n’y suis pas obligé (c’est-à-dire quand je ne suis pas au boulot et quand je ne fais pas du python), c’est pour des raisons beaucoup plus profondes. Certains anciens et/ou modérateurs doivent sûrement savoir pourquoi.

QuentinC

Okok, gardes tout de même à l’esprit que ce code de documentation est destiné à être lu par d’autres et à présenter le langage, je pense qu’il peut rentrer dans une catégorie proche de "boulot", mais bien sûr c’est ton projet et fais toi plaisir avant tout, si c’est vraiment pénible pour toi de rajouter ces indentations ça se comprend

D’autant plus que python est connu pour être lent sur les boucles.

QuentinC

Surtout en utilisant Python 2 et range qui va ici construire une liste de 10 millions d’éléments.

Mais j’ai du mal à voir l’influence de Python sur ton langage de script.

Salut !

Projet très intéressant, vu le nom du langage je m’attendais à quelque chose en relation avec Qt mais comme tu le dis, le nom n’est pas très important pour le moment !

Personnellement, j’ai une question un peu plus technique !

Je m’intéresse fortement au différence entre les langues alors penses-tu que l’usage de C++ est obligatoire ? ou penses-tu que si l’implémentation avait était faites en C#, Java cela aurait changer quelque chose ?

De plus, ou as-tu appris comment créer ton parseur etc ?

En tout cas, bravo et beau projet !

Le langage est rigolo et sympa. Mais après, j’ai du mal avec les langages expérimentaux. Car souvent, on est vite limité.

Et puis j’ai du mal à comprendre cet obsession pour un language basé sur le C++.

Sinon, le nom surprend effectivement. On pense plus à Qt qu’à Quentin pour un langage nommé QSript.

+0 -1

Bonsoir,

Réponses groupées

J’avais pas compris qu’on devait forcément le compiler, je croyais que c’était optionnel pour optimiser et obfusquer mais qu’on pouvait aussi exécuter directement le script

Comme pour python, ruby, JavaScript, lua, ou à peu près n’importe quel autre langage de script moderne, le fonctionnement est toujours le même: le code est lu, parsé, compilé en bytecode et c’est ce bytecode qui est exécuté par la VM. La compilation est en fait totalement transparente. De ton point de vue d’utilisateur ça équivaut à un langage interprété.

Certains langages permettent juste d’enregistrer le bytecode après compilation et avant exécution, pour pouvoir éviter la compilation à partir de la source au prochain chargement. Mais l’argument pour le faire c’est pas tant la vitesse ni la taille; à moins d’avoir des centaines de milliers de lignes la compilation est assez rapide. c’est plutôt une question de confidencialité je dirais

Par contre, aujourd’hui, il n’y a pour ainsi dire plus de langage qui soit interprété directement sans étape de compilation en bytecode, essentiellement parce que c’est atrocement lent.

Surtout en utilisant Python 2 et range qui va ici construire une liste de 10 millions d’éléments.

C’était avec python 3.6, pas python 2.

Je m’intéresse fortement au différence entre les langues alors penses-tu que l’usage de C++ est obligatoire ? ou penses-tu que si l’implémentation avait était faites en C#, Java cela aurait changer quelque chose ?

Ce que j’ai fait serait parfaitement faisable en Java ou en C#. J’ai surtout choisi C++ parce que c’est mon besoin initial.

Cependant, le faire en Java ou en C# changerait probablement totalement l’exercice. Dans les deux cas, je pense qu’on tenterait plutôt de faire un truc qui se compilerait en bytecode natif, pas recréer une VM qui s’exécuterait par-dessus la JVM / le CLR. Sinon ça fait VMception, ce serait sans doute un peu ridicule. En résumé ça veut dire partir sur les traces de Scala, Groovy, Cotelyn…

J’ai pas dit que ça ne serait pas intéressant, bien au contraire… mais différent.

Tiens d’ailleurs, ça serait intéressant de voir ce que font exactement Nashorn ou Jython. Nashorn, vut sa rapidité, je n’ai pas trop de doutes qu’il finit par se compiler en bytecode Java.

De plus, ou as-tu appris comment créer ton parseur etc ?

Nulle part au-delà des cours que j’ai pu avoir sur l’implémentation d’un parseur d’expressions mathématiques assez basique. Simplement en lisant des explications sur le net, du code écrit par d’autres, et bien sûr en essayant et en s’amusant soi-même. C’est ça qui est le plus important.

En tout cas, bravo et beau projet !

Merci.

Le langage est rigolo et sympa. Mais après, j’ai du mal avec les langages expérimentaux. Car souvent, on est vite limité.

Comme je l’ai dit, mon objectif est certes de l’utiliser pour scripter un jeu; mais ça reste un projet fait sur mon temps libre, tout comme le jeu en question d’ailleurs. JE suis très loin d’un python, d’un ruby ou d’un JavaScript, qui ont un fonctionnement éprouvé, une bibliothèque standard très fournie, et une bonne documentation. En même temps il y a des centaines de personnes et des organismes entiers qui travaillent derrière.

Si on regarde bien, dans tout, il y a toujours des limites. Après, en fonction de ses connaissances, de sa motivation et du temps qu’on veut bien y consacrer, on les atteint plus ou moins vite.

Et puis j’ai du mal à comprendre cet obsession pour un language basé sur le C++.

Quelle obsession ?

Sinon, le nom surprend effectivement. On pense plus à Qt qu’à Quentin pour un langage nommé QSript.

Je changerai sûrement de nom, quand j’aurai une meilleure idée, ou si quelqu’un m’en sussure une bonne.

+0 -0

Merci pour tes réponses, c’est vrai que cela pourrait être intéressant de faire un langage de scripte basé sur C# qui compile en MSIL puis exécuté par la CLR

Tu veux parler de Visual Basic .NET  ?

Comme je le comprends, pas forcément. Je ne code pas en C# mais d’après ce que j’en sais, le MSIL et la CLR sont à C# ce que sont respectivement le bytecode et la JVM en Java.

Tiens d’ailleurs, on parle régulièrement de Scala, Cotelyn ou Groovy, mais est-ce que ça existe des langages alternatifs comparables tournant sur la plate-forme .net ? Étrangement je n’ai jamais vu aucun nom… Par alternatif, j’entends, pas développé par Microsoft.

+0 -0

Bonsoir,

J’ai mis à jour. Maintenant le CLI a des classes basiques pour ouvrir/enregistrer des fichiers, et puis j’ai écrit la documentation sur comment embarquer QScript. Je vous remets ici les deux exemples que j’ai aussi mis dans embedding.md. Pour ceux qui ont déjà embarqué lua, le premier devrait sembler facile:

void add (QS::Fiber& fiber) {
double first = fiber.getNum(0); // get first number 
double second = fiber.getNum(1); // get second number 
fiber.setNum(0, first+second); // Stor the sum in slot 0, which is the return value
}

fiber.pushNativeFunction(add);
fiber.storeGlobal("add");

Mais le meilleur c’est celui-ci, avec l’interface simplifiée. Je ne pense pas qu’on puisse faire plus simple comme procédure pour enregistrer une classe C++ en QScript:

// Our Point class
struct Point {
double x, y;
Point (double x1, double y1): x(x1), y(y1) {}
double length () { return sqrt(x*x+y*y); }
Point operator+ (const Point& p) { return Point(x+p.x, y+p.y); }
};

// Register the class
fiber.registerClass<Point>("Point");

// Register the constructor
fiber.registerConstructor<Point, double, double>();

// Register the destructor. Quite useless here as our type is a POD, but still, it's better to declare it anyway.
fiber.registerDestructor();

// Register two properties x and y
fiber.registerProperty("x", PROPERTY(Point, x));
fiber.registerProperty("y", PROPERTY(Point, y));

// Register the length method
fiber.registerMethod("length", METHOD(Point, length));

// Register the + operator
fiber.registerMethod("+", METHOD(Point, operator+));

// We have finished registration, pop the Point class object
fiber.pop(); 
+0 -0

Bonsoir,

Je m’y suis remis un peu ces derniers temps. Depuis la dernière fois…

  • La doc de la bibliothèque standard est un peu plus complète
  • Ajout des décorateurs; les déclarations de variables, de classes et les paramètres de fonctions/méthodes sont décorables
  • Ajout d’opérateurs: accès null-safe .?, opérateurs custom @ (infix) et ? (préfixe unaire)

Je suis en train de chercher à ajouter quelque chose pour les grilles et matrices, parce que c’est malgré tout pas mal utilisé dans les scripts et les jeux. JE pensais par exemple ceci:

var matrix =
| 1, 0, 0 |
| 0, 1, 0 |
| 0, 0, 1 |

Je cherche toujours un autre nom pour mon petit langage, pour éviter la confusion avec QTScript.

J’ai trouvé quelques candidats potentiels mais un avis là-dessus aussi ne serait pas de refus, histoire que je ne choisisse pas de nouveau un nom déjà utilisé, trop proche d’un autre déjà utilisé, ou potentiellement confusionnant comme QScript. Voici les noms que j’ai retenus pour l’instant, celui qui me parait le plus convainquant en dernier:

  • Owl (hibou). 3 lettres, j’ai peur que ça soit compris comme un acronyme à la con si je n’en trouve pas moi-même un qui sonne bien
  • Quack (canard). Le problème c’est que ça fait penser au duck typing, ce que mon langage ne fait pas vraiment
  • Canary (canari). Peut porter à confusion avec la bêta-test de Chrome non ?
  • Hawk (faucon). Je ne me souviens plus de l’orthographe mais ça me fait vaguement penser à un utilitaire linux pour manipuler des chaînes de caractères avec des regex en bash
  • Zinc. Mouais, bof…
  • Cobalt. Proximité avec Cobol
  • Swan (cygne). Ca fait peut-être un peu prétencieux ?

Pour l’instant je reste dans les noms d’animaux ou de métaux… je trouve que ça fait bien.

Merci pour vos avis et remarques

+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte