]> git.xonotic.org Git - xonotic/darkplaces.git/blob - parser.c
Move general purpose parsing functions to a new helper library
[xonotic/darkplaces.git] / parser.c
1 /*
2 Copyright (C) 2021 David Knapp (Cloudwalk)
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "darkplaces.h"
22 #include "parser.h"
23
24 jmp_buf parse_error;
25
26 // Tell the user that their stuff is broken, why it's broken, and where it's broken, so hopefully they fix it.
27 void Parse_Error(struct qparser_state_s *state, qparser_err_t error)
28 {
29         if(!error)
30                 return;
31         else
32         { 
33                 switch (error)
34                 {
35                 case PARSE_ERR_INVAL:
36                         Con_Printf(CON_ERROR "Parse Error: %s: Unexpected token '%c', line %i, column %i\n", state->name, *state->pos, state->line, state->col);
37                         break;
38                 case PARSE_ERR_EOF:
39                         Con_Printf(CON_ERROR "Parse Error: %s: Unexpected end-of-file\n", state->name);
40                         break;
41                 default:
42                         return;
43                 }
44         }
45
46         longjmp(parse_error, 1);
47 }
48
49 // Advance forward in the stream as many times as 'count', cleanly.
50 void Parse_Next(struct qparser_state_s *state, size_t count)
51 {
52         state->col += count;
53         state->pos += count;
54
55         if(!*state->pos)
56                 Parse_Error(state, PARSE_ERR_EOF);
57 }
58
59 // Skips newlines, and handles different line endings.
60 static qbool Parse_Newline(struct qparser_state_s *state)
61 {
62         if(*state->pos == '\n')
63                 goto newline;
64         if(*state->pos == '\r')
65         {
66                 if(*state->pos + 1 == '\n')
67                         state->pos++;
68                 goto newline;
69         }
70         return false;
71 newline:
72         state->col = 1;
73         state->line++;
74         state->pos++;
75         return true;
76 }
77
78 // Skip all whitespace, as we normally know it.
79 static inline void Parse_Whitespace(struct qparser_state_s *state)
80 {
81         // TODO: Some languages enforce indentation style. Add a callback to override this.
82         while(*state->pos == ' ' || *state->pos == '\t')
83                 Parse_Next(state, 1);
84 }
85
86 // Skips the current line. Only useful for comments.
87 static inline void Parse_SkipLine(struct qparser_state_s *state)
88 {
89         while(!Parse_Newline(state))
90                 Parse_Next(state, 1);
91 }
92
93 static inline qbool Parse_Skip_Comments(struct qparser_state_s *state)
94 {
95         // Make sure these are both defined (or both not defined)
96         if((state->callback.CheckComment_Multiline_Start != NULL) ^ (state->callback.CheckComment_Multiline_End != NULL))
97                 Sys_Error("Parse_Skip_Comments: CheckComment_Multiline_Start (or _End) == NULL");
98
99         // Assume language doesn't support the respective comment types if one of these are NULL.
100         if(state->callback.CheckComment_SingleLine && state->callback.CheckComment_SingleLine(state))
101                 Parse_SkipLine(state);
102         else if(state->callback.CheckComment_Multiline_Start && state->callback.CheckComment_Multiline_Start(state))
103         {
104                 do
105                 {
106                         Parse_Next(state, 1);
107                         Parse_Newline(state);
108                 } while (state->callback.CheckComment_Multiline_End(state));
109         }
110         else
111                 return false;
112
113         return true;
114 }
115
116 // Skip all whitespace.
117 static inline void Parse_Skip(struct qparser_state_s *state)
118 {
119         /*
120          * Repeat this until we run out of whitespace, newlines, and comments.
121          * state->pos should be left on non-whitespace when this returns.
122          */
123         do {
124                 Parse_Whitespace(state);
125         } while (Parse_Skip_Comments(state) || Parse_Newline(state));
126 }
127
128 // Skip to the next token that isn't whitespace. Hopefully a valid one.
129 char Parse_NextToken(struct qparser_state_s *state)
130 {
131         /*
132          * This assumes state->pos is already on whitespace. Most of the time this
133          * doesn't happen automatically, but advancing the pointer here would break
134          * comment and newline handling when it does happen automatically.
135          */
136         Parse_Skip(state);
137         return *state->pos;
138 }