-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.js
191 lines (166 loc) · 4.15 KB
/
parser.js
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/**
* hmdoc.Parser
* written by Joel Dentici
* on 7/19/2017
*
* Parses hmdoc comments into a list of DocModules
*/
/**
* getDocComments :: string -> [string]
*
* Extracts comments from the source code and
* returns a list of comments.
*/
function getDocComments(src) {
//we need a single line for the regex matching to work
const noNewLines = src.replace(/\n/g, '<$NEWLINE$>');
//match doc comments, and then add back new lines
const matches = (noNewLines.match(/\/\*\*(.*?)\*\//g) || [])
.map(x => x.replace(/<\$NEWLINE\$>/g, '\n'));
//remove the doc comment slashes and asterisks from each comment
const comments = matches.map(x =>
x.replace(/\/\*\*/g, '')
.replace(/\*\//g, '')
.replace(/ *\*\t/g, '')
.replace(/ *\*/g, '')
.trim()
);
return comments;
}
/**
* DocFunction.new :: Object -> DocFunction
*
* Construct a DocFunction object. This is just
* convenience to fill in default values for the
* DocFunction, otherwise POJO would be used.
*/
class DocFunction {
constructor({name, hmType, description}) {
this.name = name || '';
this.hmType = hmType || '';
this.description = description || '';
}
}
/**
* DocModule.new :: Object -> DocModule
*
* Construct a DocModule object. This is just
* convenience to fill in default values for the
* DocModule, otherwise POJO would be used.
*/
class DocModule {
constructor({name, author, date, description, functions}) {
this.name = name || '';
this.author = author || [];
this.date = date || '';
this.functions = functions || [];
this.description = description || '';
}
}
/**
* parseFunction :: string -> DocFunction
*
* Parses the comment for a function and creates
* a DocFunction object that represents the comment.
*/
function parseFunction(comment) {
const funcComment = comment.split("\n");
const name = funcComment[0].substring(0,
funcComment[0].indexOf("::")).trim();
const hmType = funcComment[0].substring(
funcComment[0].indexOf("::") + 2).trim();
const description = funcComment.slice(1).map(x => x.trim()).join("\n");
return new DocFunction({
name,
hmType,
description
});
}
/**
* parseAuthor :: string -> [string]
*
* Attempts to parse the author from a line
* in a comment.
*/
function parseAuthor(line) {
if (line.startsWith("written by")) {
const authorText = line.substring("written by ".length);
const authors = authorText.split("and").map(x => x.trim());
return authors;
}
else {
return null;
}
}
/**
* parseDate :: string -> string
*
* Attempts to parse the date from a line
* in a comment.
*/
function parseDate(line) {
if (line.startsWith('on')) {
const date = line.substring('on '.length);
return date;
}
else {
return null;
}
}
/**
* parse :: ((string -> any), [string]) -> any
*
* Attempts to parse, given a parser, the provided
* lines. If any are parseable with the parser, the
* result is returned, otherwise null is returned.
*/
function parse(parser, lines) {
let result = null;
let i = 0;
while (!result && i < lines.length) {
result = parser(lines[i++]);
}
return result;
}
/**
* parseModule :: [string] -> DocModule
*
* Parses the comments into a DocModule object. This
* should include the module information as well as
* each of the parsed DocFunction comments, as DocFunctions.
*/
function parseModule(comments) {
try {
const modComment = comments[0].split("\n");
const name = modComment[0];
const author = parse(parseAuthor, modComment);
const date = parse(parseDate, modComment);
const description = modComment.slice(3).join('\n').trim();
const functions = comments.slice(1).map(parseFunction).sort(funcCmp);
return new DocModule({
name,
author,
date,
functions,
description
});
}
catch (e) { return null; }
}
function funcCmp(f1, f2) {
const name1 = f1.name, name2 = f2.name;
return name1.localeCompare(name2);
}
function modCmp(m1, m2) {
const name1 = m1.name, name2 = m2.name;
return name1.localeCompare(name2);
}
/**
* parseModules :: [string] -> [DocModule]
*
* Parses module source code into DocModules.
*/
function parseModules(srcs) {
return srcs.map(x => parseModule(getDocComments(x))).filter(x => x).sort(modCmp);
}
module.exports = parseModules;