geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

geany_json.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2014, Colomban Wendling <colomban@geany.org>
3 *
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
6 */
7/*
8 * This module contains functions for generating tags for JSON files.
9 *
10 * http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
11 *
12 * This implementation is forgiving and allows many constructs that are not
13 * actually valid but that don't conflict with the format. This is intend to
14 * better support partly broken or unfinished files.
15 */
16
17#include "general.h"
18
19#include <string.h>
20#include "debug.h"
21#include "entry.h"
22#include "keyword.h"
23#include "parse.h"
24#include "read.h"
25#include "routines.h"
26#include "vstring.h"
27
28typedef enum {
43
44typedef enum {
54
55typedef struct {
56 tokenType type;
58 vString *string;
60 unsigned long lineNumber;
62} tokenInfo;
63
64typedef enum {
69
71
73 { true, 'o', "object", "objects" },
74 { true, 'a', "array", "arrays" },
75 { true, 'n', "number", "numbers" },
76 { true, 's', "string", "strings" },
77 { true, 'b', "boolean", "booleans" },
78 { true, 'z', "null", "nulls" }
79};
80
81static tokenInfo *newToken (void)
82{
83 tokenInfo *const token = xMalloc (1, tokenInfo);
84
85 token->type = TOKEN_UNDEFINED;
86 token->scopeKind = TAG_NONE;
87 token->string = vStringNew ();
88 token->scope = vStringNew ();
91
92 return token;
93}
94
95static void deleteToken (tokenInfo *const token)
96{
97 vStringDelete (token->string);
98 vStringDelete (token->scope);
99 eFree (token);
100}
101
102static void copyToken (tokenInfo *const dest, tokenInfo *const src)
103{
104 dest->type = src->type;
105 dest->scopeKind = src->scopeKind;
106 vStringCopy (dest->string, src->string);
107 vStringCopy (dest->scope, src->scope);
108 dest->lineNumber = src->lineNumber;
109 dest->filePosition = src->filePosition;
110}
111
112static void makeJsonTag (tokenInfo *const token, const jsonKind kind)
113{
114 tagEntryInfo e;
115
116 if (! JsonKinds[kind].enabled)
117 return;
118
119 initTagEntry (&e, vStringValue (token->string), kind);
120
121 e.lineNumber = token->lineNumber;
122 e.filePosition = token->filePosition;
123
124 if (vStringLength (token->scope) > 0)
125 {
126 Assert (token->scopeKind > TAG_NONE && token->scopeKind < TAG_COUNT);
127
130 }
131
132 makeTagEntry (&e);
133}
134
135static bool isIdentChar (int c)
136{
137 return (isalnum (c) || c == '+' || c == '-' || c == '.');
138}
139
140static void readTokenFull (tokenInfo *const token,
141 bool includeStringRepr)
142{
143 int c;
144
145 token->type = TOKEN_UNDEFINED;
146 vStringClear (token->string);
147
148 do
149 c = getcFromInputFile ();
150 while (c == '\t' || c == ' ' || c == '\r' || c == '\n');
151
152 token->lineNumber = getInputLineNumber ();
154
155 switch (c)
156 {
157 case EOF: token->type = TOKEN_EOF; break;
158 case '[': token->type = TOKEN_OPEN_SQUARE; break;
159 case ']': token->type = TOKEN_CLOSE_SQUARE; break;
160 case '{': token->type = TOKEN_OPEN_CURLY; break;
161 case '}': token->type = TOKEN_CLOSE_CURLY; break;
162 case ':': token->type = TOKEN_COLON; break;
163 case ',': token->type = TOKEN_COMMA; break;
164
165 case '"':
166 {
167 bool escaped = false;
168 token->type = TOKEN_STRING;
169 while (true)
170 {
171 c = getcFromInputFile ();
172 /* we don't handle unicode escapes but they are safe */
173 if (escaped)
174 escaped = false;
175 else if (c == '\\')
176 escaped = true;
177 else if (c >= 0x00 && c <= 0x1F)
178 break; /* break on invalid, unescaped, control characters */
179 else if (c == '"' || c == EOF)
180 break;
181 if (includeStringRepr)
182 vStringPut (token->string, c);
183 }
184 break;
185 }
186
187 default:
188 if (! isIdentChar (c))
189 token->type = TOKEN_UNDEFINED;
190 else
191 {
192 do
193 {
194 vStringPut (token->string, c);
195 c = getcFromInputFile ();
196 }
197 while (c != EOF && isIdentChar (c));
199 switch (lookupKeyword (vStringValue (token->string), Lang_json))
200 {
201 case KEYWORD_true: token->type = TOKEN_TRUE; break;
202 case KEYWORD_false: token->type = TOKEN_FALSE; break;
203 case KEYWORD_null: token->type = TOKEN_NULL; break;
204 default: token->type = TOKEN_NUMBER; break;
205 }
206 }
207 break;
208 }
209}
210
211#define readToken(t) (readTokenFull ((t), false))
212
213static void pushScope (tokenInfo *const token,
214 const tokenInfo *const parent,
215 const jsonKind parentKind)
216{
217 if (vStringLength (token->scope) > 0)
218 vStringPut (token->scope, '.');
219 vStringCat (token->scope, parent->string);
220 token->scopeKind = parentKind;
221}
222
223static void popScope (tokenInfo *const token,
224 const tokenInfo *const parent)
225{
226 vStringTruncate (token->scope, vStringLength (parent->scope));
227 token->scopeKind = parent->scopeKind;
228}
229
230#define skipToOneOf2(token, type1, type2) \
231 (skipToOneOf3 (token, type1, type2, TOKEN_EOF /* dummy */))
232
233#define skipTo(token, type) \
234 (skipToOneOf3 (token, type, /* dummies */ TOKEN_EOF, TOKEN_EOF))
235
236static void skipToOneOf3 (tokenInfo *const token,
237 const tokenType type1,
238 const tokenType type2,
239 const tokenType type3)
240{
241 while (token->type != TOKEN_EOF &&
242 token->type != type1 &&
243 token->type != type2 &&
244 token->type != type3)
245 {
246 readToken (token);
247 if (token->type == TOKEN_OPEN_CURLY)
248 {
249 skipTo (token, TOKEN_CLOSE_CURLY);
250 readToken (token);
251 }
252 else if (token->type == TOKEN_OPEN_SQUARE)
253 {
254 skipTo (token, TOKEN_CLOSE_SQUARE);
255 readToken (token);
256 }
257 }
258}
259
260static jsonKind tokenToKind (const tokenType type)
261{
262 switch (type)
263 {
264 case TOKEN_OPEN_CURLY: return TAG_OBJECT;
265 case TOKEN_OPEN_SQUARE: return TAG_ARRAY;
266 case TOKEN_STRING: return TAG_STRING;
267 case TOKEN_TRUE:
268 case TOKEN_FALSE: return TAG_BOOLEAN;
269 case TOKEN_NUMBER: return TAG_NUMBER;
270 default: return TAG_NULL;
271 }
272}
273
274static void parseValue (tokenInfo *const token)
275{
276 if (token->type == TOKEN_OPEN_CURLY)
277 {
278 tokenInfo *name = newToken ();
279
280 do
281 {
282 readTokenFull (token, true);
283 if (token->type == TOKEN_STRING)
284 {
285 jsonKind tagKind = TAG_NULL; /* default in case of invalid value */
286
287 copyToken (name, token);
288
289 /* skip any possible garbage before the value */
291
292 if (token->type == TOKEN_COLON)
293 {
294 readToken (token);
295 tagKind = tokenToKind (token->type);
296
297 pushScope (token, name, tagKind);
298 parseValue (token);
299 popScope (token, name);
300 }
301
302 makeJsonTag (name, tagKind);
303 }
304 /* skip to the end of the construct */
306 }
307 while (token->type != TOKEN_EOF &&
308 token->type != TOKEN_CLOSE_CURLY);
309
310 if (token->type == TOKEN_CLOSE_CURLY)
311 readToken (token);
312
314 }
315 else if (token->type == TOKEN_OPEN_SQUARE)
316 {
317 tokenInfo *name = newToken ();
318 char buf[32];
319 unsigned int nth = 0;
320
321 readToken (token);
322 while (token->type != TOKEN_EOF &&
323 token->type != TOKEN_CLOSE_SQUARE)
324 {
325 jsonKind tagKind;
326
327 tagKind = tokenToKind (token->type);
328
329 copyToken (name, token);
330 snprintf (buf, sizeof buf, "%u", nth++);
331 vStringCopyS (name->string, buf);
332
333 makeJsonTag (name, tagKind);
334 pushScope (token, name, tagKind);
335 parseValue (token);
336 popScope (token, name);
337
338 /* skip to the end of the construct */
340 if (token->type != TOKEN_CLOSE_SQUARE)
341 readToken (token);
342 }
343
344 if (token->type == TOKEN_CLOSE_SQUARE)
345 readToken (token);
346
348 }
349}
350
351static void findJsonTags (void)
352{
353 tokenInfo *const token = newToken ();
354
355 /* We allow multiple top-level elements, although it's not actually valid
356 * JSON. An interesting side effect of this is that we allow a leading
357 * Unicode BOM mark -- even though ok, many JSON parsers will choke on it */
358 do
359 {
360 readToken (token);
361 parseValue (token);
362 }
363 while (token->type != TOKEN_EOF);
364
365 deleteToken (token);
366}
367
368static void initialize (const langType language)
369{
370 Lang_json = language;
371 addKeyword ("true", language, KEYWORD_true);
372 addKeyword ("false", language, KEYWORD_false);
373 addKeyword ("null", language, KEYWORD_null);
374}
375
376/* Create parser definition structure */
378{
379 static const char *const extensions [] = { "json", NULL };
380 parserDefinition *const def = parserNew ("JSON");
381 def->extensions = extensions;
382 def->kindTable = JsonKinds;
384 def->parser = findJsonTags;
385 def->initialize = initialize;
386
387 return def;
388}
#define Assert(c)
Definition: debug.h:47
const gchar * name
Definition: document.c:3219
int makeTagEntry(const tagEntryInfo *const tag)
Definition: entry.c:1675
void initTagEntry(tagEntryInfo *const e, const char *const name, int kindIndex)
Definition: entry.c:1823
tokenType
Definition: geany_css.c:42
#define skipToOneOf2(token, type1, type2)
Definition: geany_json.c:230
static tokenInfo * newToken(void)
Definition: geany_json.c:81
static void parseValue(tokenInfo *const token)
Definition: geany_json.c:274
static void skipToOneOf3(tokenInfo *const token, const tokenType type1, const tokenType type2, const tokenType type3)
Definition: geany_json.c:236
keywordId
Definition: geany_json.c:64
@ KEYWORD_true
Definition: geany_json.c:65
@ KEYWORD_false
Definition: geany_json.c:66
@ KEYWORD_null
Definition: geany_json.c:67
static void copyToken(tokenInfo *const dest, tokenInfo *const src)
Definition: geany_json.c:102
#define readToken(t)
Definition: geany_json.c:211
static jsonKind tokenToKind(const tokenType type)
Definition: geany_json.c:260
static langType Lang_json
Definition: geany_json.c:70
#define skipTo(token, type)
Definition: geany_json.c:233
static kindDefinition JsonKinds[]
Definition: geany_json.c:72
static void pushScope(tokenInfo *const token, const tokenInfo *const parent, const jsonKind parentKind)
Definition: geany_json.c:213
tokenType
Definition: geany_json.c:28
@ TOKEN_CLOSE_CURLY
Definition: geany_json.c:34
@ TOKEN_OPEN_CURLY
Definition: geany_json.c:33
@ TOKEN_TRUE
Definition: geany_json.c:37
@ TOKEN_CLOSE_SQUARE
Definition: geany_json.c:32
@ TOKEN_UNDEFINED
Definition: geany_json.c:30
@ TOKEN_FALSE
Definition: geany_json.c:38
@ TOKEN_OPEN_SQUARE
Definition: geany_json.c:31
@ TOKEN_COMMA
Definition: geany_json.c:36
@ TOKEN_COLON
Definition: geany_json.c:35
@ TOKEN_EOF
Definition: geany_json.c:29
@ TOKEN_NUMBER
Definition: geany_json.c:40
@ TOKEN_NULL
Definition: geany_json.c:39
@ TOKEN_STRING
Definition: geany_json.c:41
parserDefinition * JsonParser(void)
Definition: geany_json.c:377
static void deleteToken(tokenInfo *const token)
Definition: geany_json.c:95
jsonKind
Definition: geany_json.c:44
@ TAG_COUNT
Definition: geany_json.c:52
@ TAG_NUMBER
Definition: geany_json.c:48
@ TAG_BOOLEAN
Definition: geany_json.c:50
@ TAG_STRING
Definition: geany_json.c:49
@ TAG_NULL
Definition: geany_json.c:51
@ TAG_OBJECT
Definition: geany_json.c:46
@ TAG_ARRAY
Definition: geany_json.c:47
@ TAG_NONE
Definition: geany_json.c:45
static void readTokenFull(tokenInfo *const token, bool includeStringRepr)
Definition: geany_json.c:140
static void findJsonTags(void)
Definition: geany_json.c:351
static void initialize(const langType language)
Definition: geany_json.c:368
static void makeJsonTag(tokenInfo *const token, const jsonKind kind)
Definition: geany_json.c:112
static bool isIdentChar(int c)
Definition: geany_json.c:135
static void popScope(tokenInfo *const token, const tokenInfo *const parent)
Definition: geany_json.c:223
void addKeyword(const char *const string, langType language, int value)
Definition: keyword.c:108
int lookupKeyword(const char *const string, langType language)
Definition: keyword.c:160
parserDefinition * parserNew(const char *name)
Definition: parse.c:237
#define NULL
Definition: rbtree.h:150
unsigned long getInputLineNumber(void)
Definition: read.c:142
int getcFromInputFile(void)
Definition: read.c:923
MIOPos getInputFilePosition(void)
Definition: read.c:161
void ungetcToInputFile(int c)
Definition: read.c:813
void eFree(void *const ptr)
Definition: routines.c:252
#define xMalloc(n, Type)
Definition: routines.h:23
#define ARRAY_SIZE(X)
Definition: routines.h:27
MIOPos:
Definition: mio.h:101
parserInitialize initialize
Definition: parse.h:81
const char *const * extensions
Definition: parse.h:78
simpleParser parser
Definition: parse.h:83
kindDefinition * kindTable
Definition: parse.h:76
unsigned int kindCount
Definition: parse.h:77
struct sTagEntryInfo::@3 extensionFields
MIOPos filePosition
Definition: entry.h:60
unsigned long lineNumber
Definition: entry.h:56
int scopeKindIndex
Definition: entry.h:78
const char * scopeName
Definition: entry.h:79
jsonKind scopeKind
Definition: geany_json.c:57
tokenType type
Definition: geany_css.c:50
vString * string
Definition: geany_css.c:51
vString * scope
Definition: geany_json.c:59
MIOPos filePosition
Definition: geany_json.c:61
unsigned long lineNumber
Definition: geany_json.c:60
struct sTokenInfo tokenInfo
int langType
Definition: types.h:13
void vStringCopyS(vString *const string, const char *const s)
Definition: vstring.c:213
vString * vStringNew(void)
Definition: vstring.c:70
void vStringDelete(vString *const string)
Definition: vstring.c:60
void vStringTruncate(vString *const string, const size_t length)
Definition: vstring.c:51
void vStringCat(vString *const string, const vString *const s)
Definition: vstring.c:139
void vStringCopy(vString *const string, const vString *const s)
Definition: vstring.c:207
#define vStringClear(string)
Definition: vstring.h:36
#define vStringLength(vs)
Definition: vstring.h:31
#define vStringValue(vs)
Definition: vstring.h:28
static void vStringPut(vString *const string, const int c)
Definition: vstring.h:101