Merge pull request #41 from blackducksw/ubuntu_14
[ohcount] / src / parsers / javascript.rl
1 // Javascript.rl written by Mitchell Foral. mitchell<att>caladbolg<dott>net.
2
3 /************************* Required for every parser *************************/
4 #ifndef OHCOUNT_JAVASCRIPT_PARSER_H
5 #define OHCOUNT_JAVASCRIPT_PARSER_H
6
7 #include "../parser_macros.h"
8
9 // the name of the language
10 const char *JS_LANG = LANG_JAVASCRIPT;
11
12 // the languages entities
13 const char *js_entities[] = {
14   "space", "comment", "string", "number", "keyword",
15   "identifier", "operator", "any"
16 };
17
18 // constants associated with the entities
19 enum {
20   JS_SPACE = 0, JS_COMMENT, JS_STRING, JS_NUMBER, JS_KEYWORD,
21   JS_IDENTIFIER, JS_OPERATOR, JS_ANY
22 };
23
24 /*****************************************************************************/
25
26 %%{
27   machine javascript;
28   write data;
29   include common "common.rl";
30
31   # Line counting machine
32
33   action js_ccallback {
34     switch(entity) {
35     case JS_SPACE:
36       ls
37       break;
38     case JS_ANY:
39       code
40       break;
41     case INTERNAL_NL:
42       std_internal_newline(JS_LANG)
43       break;
44     case NEWLINE:
45       std_newline(JS_LANG)
46     }
47   }
48
49   js_line_comment = '//' @comment nonnewline*;
50   js_block_comment =
51     '/*' @comment (
52       newline %{ entity = INTERNAL_NL; } %js_ccallback
53       |
54       ws
55       |
56       (nonnewline - ws) @comment
57     )* :>> '*/';
58   js_comment = js_line_comment | js_block_comment;
59
60   # Does Javascript allow newlines inside strings?
61   # I can't find a definitive answer.
62   js_sq_str =
63     '\'' @code (
64       escaped_newline %{ entity = INTERNAL_NL; } %js_ccallback
65       |
66       ws
67       |
68       [^\t '\\] @code
69       |
70       '\\' nonnewline @code
71     )* '\'';
72   js_dq_str =
73     '"' @code (
74       escaped_newline %{ entity = INTERNAL_NL; } %js_ccallback
75       |
76       ws
77       |
78       [^\t "\\] @code
79       |
80       '\\' nonnewline @code
81     )* '"';
82   js_regex_str = '/' [^/*] ([^\r\n\f/\\] | '\\' nonnewline)* '/' @code;
83   js_string = js_sq_str | js_dq_str | js_regex_str;
84
85   js_line := |*
86     spaces     ${ entity = JS_SPACE; } => js_ccallback;
87     js_comment;
88     js_string;
89     newline    ${ entity = NEWLINE;  } => js_ccallback;
90     ^space     ${ entity = JS_ANY;   } => js_ccallback;
91   *|;
92
93   # Entity machine
94
95   action js_ecallback {
96     callback(JS_LANG, js_entities[entity], cint(ts), cint(te), userdata);
97   }
98
99   js_line_comment_entity = '//' nonnewline*;
100   js_block_comment_entity = '/*' any* :>> '*/';
101   js_comment_entity = js_line_comment_entity | js_block_comment_entity;
102
103   js_entity := |*
104     space+            ${ entity = JS_SPACE;   } => js_ecallback;
105     js_comment_entity ${ entity = JS_COMMENT; } => js_ecallback;
106     # TODO:
107     ^space;
108   *|;
109 }%%
110
111 /* Parses a string buffer with Javascript code.
112  *
113  * @param *buffer The string to parse.
114  * @param length The length of the string to parse.
115  * @param count Integer flag specifying whether or not to count lines. If yes,
116  *   uses the Ragel machine optimized for counting. Otherwise uses the Ragel
117  *   machine optimized for returning entity positions.
118  * @param *callback Callback function. If count is set, callback is called for
119  *   every line of code, comment, or blank with 'lcode', 'lcomment', and
120  *   'lblank' respectively. Otherwise callback is called for each entity found.
121  */
122 void parse_javascript(char *buffer, int length, int count,
123                       void (*callback) (const char *lang, const char *entity,
124                                         int s, int e, void *udata),
125                       void *userdata
126   ) {
127   init
128
129   %% write init;
130   cs = (count) ? javascript_en_js_line : javascript_en_js_entity;
131   %% write exec;
132
133   // if no newline at EOF; callback contents of last line
134   if (count) { process_last_line(JS_LANG) }
135 }
136
137 const char *QML_LANG = LANG_QML;
138 const char *ORIG_JS_LANG = LANG_JAVASCRIPT;
139 void parse_qml(char *buffer, int length, int count,
140                void (*callback) (const char *lang, const char *entity,
141                                  int s, int e, void *udata),
142                void *userdata
143   ) {
144   JS_LANG = QML_LANG;
145   parse_javascript(buffer, length, count, callback, userdata);
146   JS_LANG = ORIG_JS_LANG;
147 }
148
149 #endif