1 /*
2 *
3 * Copyright (c) 2022, Masatake YAMATO
4 *
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License version 2 or (at your option) any later version.
7 *
8 * This module contains functions for generating tags for R Markdown files.
9 * https://bookdown.org/yihui/rmarkdown/
10 *
11 */
12
13 /*
14 * INCLUDE FILES
15 */
16 #include "general.h" /* must always come first */
17 #include "markdown.h"
18
19 #include "entry.h"
20 #include "parse.h"
21
22 #include <ctype.h>
23 #include <string.h>
24
25 /*
26 * DATA DEFINITIONS
27 */
28 typedef enum {
29 K_CHUNK_LABEL = 0,
30 } rmarkdownKind;
31
32 static kindDefinition RMarkdownKinds[] = {
33 { true, 'l', "chunklabel", "chunk labels"},
34 };
35
36 struct sRMarkdownSubparser {
37 markdownSubparser markdown;
38 };
39
40 /*
41 * FUNCTION DEFINITIONS
42 */
43
findRMarkdownTags(void)44 static void findRMarkdownTags (void)
45 {
46 scheduleRunningBaseparser (0);
47 }
48
49 #define skip_space(CP) while (*CP == ' ' || *CP == '\t') CP++;
50
makeRMarkdownTag(vString * name,int kindIndex,bool anonymous)51 static void makeRMarkdownTag (vString *name, int kindIndex, bool anonymous)
52 {
53 tagEntryInfo e;
54 initTagEntry (&e, vStringValue (name), kindIndex);
55 if (anonymous)
56 markTagExtraBit (&e, XTAG_ANONYMOUS);
57 makeTagEntry (&e);
58 }
59
extractLanguageForCodeBlock(markdownSubparser * s,const char * langMarker,vString * langName)60 static bool extractLanguageForCodeBlock (markdownSubparser *s,
61 const char *langMarker,
62 vString *langName)
63 {
64 const char *cp = langMarker;
65
66 if (*cp != '{')
67 return false;
68 cp++;
69
70 const char *end = strpbrk(cp, " \t,}");
71 if (!end)
72 return false;
73
74 if (end - cp == 0)
75 return false;
76
77 vStringNCatS (langName, cp, end - cp);
78
79 cp = end;
80 if (*cp == ',' || *cp == '}')
81 {
82 vString *name = anonGenerateNew("__anon", K_CHUNK_LABEL);
83 makeRMarkdownTag (name, K_CHUNK_LABEL, true);
84 vStringDelete (name);
85 return true;
86 }
87
88 skip_space(cp);
89
90 vString *chunk_label = vStringNew ();
91 bool anonymous = false;
92 while (isalnum((unsigned char)*cp) || *cp == '-')
93 vStringPut (chunk_label, *cp++);
94
95 if (vStringLength (chunk_label) == 0)
96 {
97 anonGenerate (chunk_label, "__anon", K_CHUNK_LABEL);
98 anonymous = true;
99 }
100
101 skip_space(cp);
102 if (*cp == ',' || *cp == '}')
103 makeRMarkdownTag (chunk_label, K_CHUNK_LABEL, anonymous);
104
105 vStringDelete (chunk_label);
106 return true;
107 }
108
RMarkdownParser(void)109 extern parserDefinition* RMarkdownParser (void)
110 {
111 static const char *const extensions [] = { "rmd", NULL };
112 static struct sRMarkdownSubparser rmarkdownSubparser = {
113 .markdown = {
114 .subparser = {
115 .direction = SUBPARSER_SUB_RUNS_BASE,
116 },
117 .extractLanguageForCodeBlock = extractLanguageForCodeBlock,
118 },
119 };
120 static parserDependency dependencies [] = {
121 [0] = { DEPTYPE_SUBPARSER, "Markdown", &rmarkdownSubparser },
122 };
123
124 parserDefinition* const def = parserNew ("RMarkdown");
125
126
127 def->dependencies = dependencies;
128 def->dependencyCount = ARRAY_SIZE(dependencies);
129 def->kindTable = RMarkdownKinds;
130 def->kindCount = ARRAY_SIZE (RMarkdownKinds);
131 def->extensions = extensions;
132 def->parser = findRMarkdownTags;
133 return def;
134 }
135