Bonjour à tous,

Je développe un interpréteur de classes et interfaces (style POO de PHP), le JSCN (JavaScript Class Notation).


Ce que j'ai déjà fait (100% fonctionnel) :

  • Un simulacre de parser
  • Tout le système de chargement, d'interprétation et construction des classes, en JS
  • Le type hinting via des constructeurs



Ce qui me reste à faire :

  • Le type hinting classique (void, int, double, etc.)
  • Les valeurs par défaut des arguments de méthodes
  • La gestion des erreurs personnalisées, propres à mon interpréteur (ParseError, liées à l'héritages, etc.)
  • La finalisation de la gestion des interfaces
  • Un parser digne de ce nom, le moins gourmand possible



C'est donc pour développer un parser digne de ce nom que je me tourne vers vous, car c'est tout ce qu'il me manque pour terminer mon projet.

Je recherche des conseils précis sur l'approche de son fonctionnement, qui parse un code hybride PHP-JS.

Le but est donc, ici, de dresser une "ligne de conduite"...

Voici la source de mon simulacre de parser actuel :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
var parser;
parser={
    patterns:{
        blockStart:/\r?\n\t*\{/g,
        carriageReturn:/\r?\n/,
        commentLine:/^\s*\/\//,
        commentStart:/^\s*\/\*/,
        commentEnd:/\*\/\s*$/,
        trimLeft:/^\s+/,
        trimRight:/\s+$/,
        classHead:/^(?:(final|abstract)\s+)?class\s+([A-Z][\w\d]*(?:_[A-Z][\w\d]*)*)(?:\s+extends\s+([A-Z][\w\d]*(?:_[A-Z][\w\d]*)*))?(?:\s+implements\s+((?:(?:[A-Z][\w\d]*(?:_[A-Z][\w\d]*)*)(?:\s*,\s*)?)+)?)?\s*\{?/,
        interfaceHead:/^interface\s+([A-Z][\w\d]*(?:_[A-Z][\w\d]*)*)(?:\s+extends\s+([A-Z][\w\d]*(?:_[A-Z][\w\d]*)*))?\s*\{?/,
        interfaceNames:/\s*,\s*/g,
        property:/^(?:([A-Z][\w\d\$_]*)\s+)?(?:\s*(public|protected|private))(?:\s+(static))?(?:\s+(const))?\s+([a-z][\w\d\$_]*)(?:\s*=\s*(.+?))?\s*;/,
        classMethod:/^(?:\s*([A-Z][\w\d\$_]*)\s+)?(?:\s*(final)\s+)?(?:\s*(public|protected|private))(?:\s+(static))?\s+function\s+([a-z][\w\d\$_]*)\s*\(\s*([^\)]*)\s*\)\s*\{?/,
        interfaceMethod:/^(?:\s*([A-Z][\w\d\$_]*)\s+)?\s*public(?:\s+(static))?\s+function\s+([a-z][\w\d\$_]*)\s*\(\s*([^\)]*)\s*\)\s*;/,
        token:/^(?:\s*[A-Z][\w\d\$_]*\s+)?(?:\s*final\s+)?(?:\s*public|protected|private)/,
        argStr:/\s*(?:(?:([A-Z][\w\d\$\._]*)*\s+([a-z_][\w\d\$_]*))|([a-z_][\w\d\$_]*))+?/g
    },
    parse:function parse(name,type,source){
        var line,
            lines,
            iterator,
            length,
            matches,
            itemInfo,
            property,
            methodLines;
        lines=parser.stripComments(source.replace(parser.patterns.blockStart,'{').split(parser.patterns.carriageReturn));
        iterator=0;
        length=lines.length-1;
        if(type==='class'&&(matches=lines[0].match(parser.patterns.classHead))){
            itemInfo={
                type:type,
                source:source,
                ns:name.substring(name.lastIndexOf('_')+1),
                isFinal:matches[1]==='final',
                isAbstract:matches[1]==='abstract',
                name:matches[2],
                parent:matches[3]!==''?matches[3]:null,
                interfaces:matches[4]!==''?matches[4].split(parser.patterns.interfaceNames):null,
                properties:[],
                descriptor:{}
            };
        }
        else if(type==='interface'&&(matches=lines[0].match(parser.patterns.interfaceHead))){
            itemInfo={
                type:type,
                name:matches[1],
                ns:name.substring(name.lastIndexOf('_')+1),
                parent:matches[2]!==''?matches[2]:null,
                properties:[]
            };
        }
        while((iterator+=1)<length){
            line=lines[iterator];
            property=[];
            if((matches=line.match(parser.patterns.property))){
                property.name=matches[5];
                property.type=matches[1];
                property.visibility=matches[2];
                property.isStatic=!!matches[3];
                property.isConst=!!matches[4];
                property.value=matches[6];
            }
            else if(type==='class'&&(matches=line.match(parser.patterns.classMethod))){
                methodLines=[];
                property.name=matches[5];
                property.returnType=matches[1];
                property.isFinal=!!matches[2];
                property.visibility=matches[3];
                property.isStatic=!!matches[4];
                property.argStr=matches[6];
                while(!('body' in property)){
                    if(lines[iterator+2]!==void null&&!(parser.patterns.token.test(lines[iterator+1]))){
                        if(lines[iterator]!==line){
                            methodLines.push(lines[iterator]);
                        }
                        iterator+=1;
                    }
                    else{
                        property.body=methodLines.join('\n');
                    }
                }
            }
            else if(type==='interface'&&(matches=line.match(parser.patterns.interfaceMethod))){
                property.name=matches[3];
                property.returnType=matches[1];
                property.visibility='public';
                property.isStatic=!!matches[2];
                property.argStr=matches[4];
            }
            if(typeof property.name!=='undefined'){
                itemInfo.properties[property.name]=property;
            }
        }
        return itemInfo;
    },
    stripComments:function stripComments(lines){
        var line,
            length,
            patterns,
            newLines,
            iterator,
            level;
        newLines=[];
        iterator=-1;
        level=0;
        length=lines.length;
        while((iterator+=1)<length){
            line=lines[iterator];
            if(!(parser.patterns.commentLine.test(line))){
                if(parser.patterns.commentStart.test(line)){
                    level+=1;
                }
                if(level===0){
                    line=parser.trim(line);
                    if(line.length>0){
                        newLines.push(line);
                    }
                }
                else{
                    if(parser.patterns.commentEnd.test(line)){
                        level-=1;
                    }
                }
            }
        }
        return newLines;
    },
    trim:function trim(str){
        return str.replace(parser.patterns.trimLeft,'').replace(parser.patterns.trimRight,'');
    }
};
Au final, le parser doit pouvoir :

  • Lire la structure d'une classe/interface comme si elle était écrite en PHP
  • Détecter les erreurs de syntaxe JS (dans les méthodes)
  • Remplacer les références au namespace, s'il y en a
  • "Enregistrer" les lignes, à des fins de debugging


Est-ce que quelqu'un pourrait me guider vers son optimisation, étape par étape, svp? (ne me renvoyez pas vers un tuto pour créer un parser en C, par exemple, je ne connais rien à ce langage, alors encore moins à ses outils).

PS : Je ne comprends strictement rien aux grammaires, je n'ai pas réussi à trouver un tuto détaillé sur leur fonctionnement, avec des exemples pratiques... (genre "Créer un parser pour les nuls")