Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_compile_parse.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * Adaptive Framework Compiler Parser
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
14 #include "afw_internal.h"
15 
16 
17 
18 /*ebnf>>>
19  *
20  *# This grammar uses W3C (https://www.w3.org/) EBNF notation.
21  *#
22  *# Note: This grammar is intended as documentation and is not
23  *# precise.
24  *#
25  *# Directory src/afw/doc/syntax/ must be produced manually from
26  *# src/afw/generated/ebnf/syntax.ebnf using the following steps:
27  *#
28  *# 1) Visit http://bottlecaps.de/rr/ui in a web browser.
29  *# 2) Optionally, go to the "Options" tab and change Color to one
30  *# compatible with the admin app such as #DCDCDC which is shades
31  *# of gray.
32  *# 3) Paste the contents of src/afw/generated/syntax/compile.ebnf in
33  *# the text area of the "Edit Grammar" tab.
34  *# 4) Select the "View Diagram" tab then click the PNG radio button
35  *# in "Download diagram" box in the top right corner and press
36  *# the "Download" button.
37  *# 5) Replace the contents of the syntax/ directory with the contents
38  *# of the downloaded zip file.
39  *#
40  *# Root productions
41  *#
42  *# A parameter passed to the adaptive compiler determines the first
43  *# production used for parse.
44  *#
45  *# Expression - afw_compile_type_expression
46  *#
47  *# Hybrid - afw_compile_type_hybrid
48  *#
49  *# Json - afw_compile_type_json and afw_compile_type_relaxed_json
50  *#
51  *# ParenthesizedExpression - afw_compile_type_parenthesized_expression
52  *#
53  *# Script - afw_compile_type_script
54  *#
55  *# Template - afw_compile_type_template
56  *#
57  *
58  *<<<ebnf*/
59 
60 
61 
63 afw_compile_create_contextual(
64  afw_compile_parser_t* parser,
65  afw_size_t start_offset,
66  afw_size_t size)
67 {
69 
71  parser->xctx);
72 
73  result->compiled_value = parser->compiled_value;
74  result->source_location = parser->contextual.source_location;
75  result->value_offset = start_offset;
76  result->value_size = size;
77  return result;
78 }
79 
80 
81 
83 afw_compile_create_source_location_impl(
84  afw_compile_parser_t *parser,
85  afw_size_t start_offset)
86 {
87  const afw_utf8_t *result;
88  afw_size_t line;
89  afw_size_t column;
90  afw_boolean_t has_nl;
91 
93  &line, &column,
94  parser->full_source, start_offset,
95  4, parser->xctx);
96 
97  if (has_nl) {
98  result = afw_utf8_printf(parser->p, parser->xctx,
99  "%" AFW_UTF8_FMT "[%" AFW_SIZE_T_FMT ":%" AFW_SIZE_T_FMT "]",
100  AFW_UTF8_FMT_ARG(parser->contextual.source_location),
101  line, column);
102  }
103  else {
104  result = afw_utf8_printf(parser->p, parser->xctx,
105  "%" AFW_UTF8_FMT "+%" AFW_SIZE_T_FMT,
106  AFW_UTF8_FMT_ARG(parser->contextual.source_location),
107  start_offset);
108  }
109 
110  return result;
111 }
112 
113 
114 
115 /* Reference create. */
117 afw_compile_parse_reference_create(
118  afw_compile_parser_t *parser,
119  const afw_compile_value_contextual_t *contextual,
120  const afw_utf8_t *identifier)
121 {
122  const afw_value_t *result;
123  afw_value_block_symbol_t *symbol;
124  afw_utf8_t qualifier;
125  afw_utf8_t name;
126 
127  result = NULL;
128 
129  afw_compile_split_qualified_name(parser->token->string,
130  &qualifier, &name, parser->xctx);
131  if (qualifier.len == 0) {
132  symbol = afw_compile_parse_get_symbol_entry(parser, &name);
133  if (symbol) {
135  contextual, symbol, parser->p, parser->xctx);
136  }
137  }
138  if (!result) {
139  result = (const afw_value_t *)
141  afw_utf8_equal(&qualifier, &afw_s_fn) ? NULL : &qualifier,
142  &name, parser->xctx);
143  if (!result) {
144  if (qualifier.len == 0) {
145  AFW_COMPILE_THROW_ERROR_FZ(
146  "Undeclared variable %" AFW_UTF8_FMT,
147  AFW_UTF8_FMT_ARG(&name));
148  }
149  if (afw_utf8_equal(&qualifier, &afw_s_fn)) {
150  AFW_COMPILE_THROW_ERROR_FZ(
151  "Unknown built-in function %" AFW_UTF8_FMT,
152  AFW_UTF8_FMT_ARG(&name));
153  }
154  result =
156  contextual, &qualifier, &name,
157  parser->p, parser->xctx);
158  }
159  }
160 
161  return result;
162 }
163 
164 
165 
166 /* Parse error. */
168 afw_compile_parse_set_error_z(
169  afw_compile_parser_t *parser,
170  const afw_utf8_z_t *source_z,
171  const afw_utf8_z_t *message_z)
172 {
173  afw_size_t line;
174  afw_size_t column;
175 
176  afw_compile_set_contextual();
178  &line, &column,
179  parser->full_source, parser->token->token_source_offset,
180  4, parser->xctx);
181 
182  afw_error_set_fz(afw_error_code_syntax,
183  source_z, parser->xctx,
184  "Parse error at offset "
185  "%" AFW_SIZE_T_FMT
186  " around line "
187  "%" AFW_SIZE_T_FMT
188  " column "
189  "%" AFW_SIZE_T_FMT
190  ": %s",
191  parser->token->token_source_offset, line, column, message_z);
192 
193  parser->xctx->error->parser_source = parser->full_source;
194 }
195 
196 
197 
198 /* Parse error. */
200 afw_compile_parse_set_error_fz(
201  afw_compile_parser_t *parser,
202  const afw_utf8_z_t *source_z,
203  const afw_utf8_z_t *format_z, ...)
204 {
205  va_list ap;
206  const afw_utf8_z_t *message_z;
207 
208  va_start(ap, format_z);
209  message_z = afw_utf8_z_printf_v(format_z, ap,
210  parser->p, parser->xctx);
211  va_end(ap);
212 
213  afw_compile_parse_set_error_z(parser, source_z, message_z);
214 }
215 
216 
218 afw_compile_parse_variable_reference_create(
219  afw_compile_parser_t *parser,
220  const afw_compile_value_contextual_t *contextual,
222  const afw_utf8_t *identifier)
223 {
224  afw_value_block_symbol_t *symbol;
225 
226  if (assignment_type == afw_compile_assignment_type_loc) {
227  symbol = afw_compile_parse_add_symbol_entry(parser, identifier);
228  }
229  else if (assignment_type == afw_compile_assignment_type_const) {
230  symbol = afw_compile_parse_add_symbol_entry(parser, identifier);
231  }
232  else if (assignment_type == afw_compile_assignment_type_reference_only) {
233  symbol = afw_compile_parse_get_symbol_entry(parser, identifier);
234  if (!symbol) {
235  AFW_COMPILE_THROW_ERROR_FZ(
236  "Variable %" AFW_UTF8_FMT " is not declared",
237  AFW_UTF8_FMT_ARG(identifier));
238  }
239  }
240  else {
241  symbol = afw_compile_parse_get_symbol_entry(parser, identifier);
242  if (!symbol) {
243  AFW_COMPILE_THROW_ERROR_FZ(
244  "Variable %" AFW_UTF8_FMT " is not declared",
245  AFW_UTF8_FMT_ARG(identifier));
246  }
247  }
248 
249  return (const afw_value_variable_reference_t *)
251  contextual, symbol, parser->p, parser->xctx);
252 }
253 
254 
255 
257 afw_compile_parse_get_symbol_entry(
258  afw_compile_parser_t *parser,
259  const afw_utf8_t *name)
260 {
261  afw_value_block_t *block;
263 
264  for (block = parser->compiled_value->current_block;
265  block;
266  block = block->parent_block)
267  {
268  for (entry = block->first_entry; entry; entry = entry->next_entry)
269  {
270  if (afw_utf8_equal(entry->name, name)) {
271  return entry;
272  }
273  }
274  }
275 
276  return NULL;
277 }
278 
279 
280 
282 afw_compile_parse_get_local_symbol_entry(
283  afw_compile_parser_t *parser,
284  const afw_utf8_t *name)
285 {
287 
288  for (entry = parser->compiled_value->current_block->first_entry;
289  entry;
290  entry = entry->next_entry)
291  {
292  if (afw_utf8_equal(entry->name, name)) {
293  return entry;
294  }
295  }
296 
297  return entry;
298 }
299 
300 
301 
303 afw_compile_parse_add_symbol_entry(
304  afw_compile_parser_t *parser,
305  const afw_utf8_t *name)
306 {
308 
309  if (afw_compile_parse_get_local_symbol_entry(parser, name)) {
310  AFW_COMPILE_THROW_ERROR_FZ("%" AFW_UTF8_FMT " already defined",
311  AFW_UTF8_FMT_ARG(name));
312  }
313 
314  entry = afw_pool_calloc_type(parser->p,
315  afw_value_block_symbol_t, parser->xctx);
316  entry->name = name;
317  entry->parent_block = parser->compiled_value->current_block;
318  if (!parser->compiled_value->current_block->first_entry) {
319  parser->compiled_value->current_block->first_entry = entry;
320  parser->compiled_value->current_block->final_entry = entry;
321  }
322  else {
323  entry->index = parser->compiled_value->current_block->entry_count;
324  parser->compiled_value->current_block->final_entry->next_entry = entry;
325  parser->compiled_value->current_block->final_entry = entry;
326  }
327  parser->compiled_value->current_block->entry_count++;
328 
329  return entry;
330 }
331 
332 
333 
335 afw_compile_parse_link_new_value_block(
336  afw_compile_parser_t *parser,
337  afw_size_t start_offset)
338 {
339  const afw_value_block_t *block;
340  const afw_compile_value_contextual_t *contextual;
341 
342  contextual = afw_compile_create_contextual_to_cursor(start_offset);
343 
345  contextual,
346  parser->compiled_value,
347  &parser->block_count,
348  parser->p, parser->xctx);
349 
350  return block;
351 }
352 
353 
354 
356 afw_compile_parse_check_symbol(
357  afw_compile_parser_t *parser,
358  const afw_utf8_t *name,
359  const afw_value_t *value,
361  afw_size_t symbol_cursor)
362 {
363 
364 }
365 
366 
367 
368 /*ebnf>>>
369  *
370  *# Hybrid is used to parse data type hybrid.
371  *#
372  *# A hybrid is parsed as a Script, ExpressionTuple, Template, or an evaluated
373  *# string as follows:
374  *#
375  *# 1) If it begins with a '#!', it is parsed as a Script.
376  *# 2) If it begins with '[', it is parsed as an ExpressionTuple.
377  *# 3) Otherwise, it is parsed as a Template. Note that if the
378  *# template does not contain '${', it produces an evaluated string.
379  *#
380  *
381  * Hybrid ::= Script | ExpressionTuple | Template
382  *
383  *<<<ebnf*/
385 afw_compile_parse_Hybrid(afw_compile_parser_t *parser)
386 {
387  afw_code_point_t cp;
388  afw_utf8_t line;
389 
390  /* Note: if full_source_type is hybrid (top call), make it more specific. */
391 
392  /* If starts with shebang, this is an adaptive script. */
393  if (afw_compile_next_raw_starts_with_z("#!")) {
394  afw_compile_get_raw_line(&line);
395  if (!afw_utf8_contains(&line, &afw_s_afw) &&
396  !afw_utf8_contains(&line, &afw_s_maluba))
397  {
399  "Shebang line must contain afw or maluba to be "
400  "recognized as an adaptive script in a hybrid value");
401  }
402 
403  if (parser->compiled_value->full_source_type == &afw_s_hybrid) {
404  parser->compiled_value->full_source_type = &afw_s_script;
405  }
406 
407  return afw_compile_parse_list_of_statements(parser, false, true);
408  }
409 
410  /* Get first codepoint. */
411  cp = afw_compile_peek_code_point(parser);
412 
413  /* If no input, return empty string. */
414  if (cp < 0) {
415  if (parser->compiled_value->full_source_type == &afw_s_hybrid) {
416  parser->compiled_value->full_source_type = &afw_s_string;
417  }
418  return afw_value_empty_string;
419  }
420 
421  /* If this starts with '[', parse as ExpressionTuple. */
422  if (cp == '[') {
423  if (parser->compiled_value->full_source_type == &afw_s_hybrid) {
424  parser->compiled_value->full_source_type = &afw_s_expression_tuple;
425  }
426  return afw_compile_parse_ExpressionTuple(parser);
427  }
428 
429 
430  /*
431  * Otherwise, return parser template. Template is always one line, so
432  * force residual check to none and callback to NULL.
433  */
434  parser->residual_check = afw_compile_residual_check_none;
435  parser->callback = NULL;
436  if (parser->compiled_value->full_source_type == &afw_s_hybrid) {
437  parser->compiled_value->full_source_type = &afw_s_template;
438  }
439  return afw_compile_parse_Template(parser);
440 }
#define AFW_DEFINE_INTERNAL(type)
Define an internal function for /src/afw/ source*.c files.
Adaptive Framework Core Internal.
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
_Bool afw_boolean_t
Definition: afw_common.h:373
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
afw_int32_t afw_code_point_t
Unicode code point.
Definition: afw_common.h:205
afw_utf8_octet_t afw_utf8_z_t
NFC normalized UTF-8 null terminated string.
Definition: afw_common.h:523
apr_size_t afw_size_t
size_t.
Definition: afw_common.h:151
#define AFW_SIZE_T_FMT
Format string specifier used for afw_size_t.
Definition: afw_common.h:341
afw_compile_internal_assignment_type_t
Enum for assignment types.
#define AFW_COMPILE_THROW_ERROR_Z(message_z)
afw_compile_split_qualified_name(const afw_utf8_t *qualified_name, afw_utf8_t *qualifier, afw_utf8_t *name, afw_xctx_t *xctx)
Split name with optional qualifier.
Definition: afw_compile.c:548
afw_environment_get_qualified_function(const afw_utf8_t *qualifier, const afw_utf8_t *name, afw_xctx_t *xctx)
Get the qualified function instance.
#define afw_pool_calloc_type(instance, type, xctx)
Macro to allocate cleared memory to hold type in pool.
Definition: afw_pool.h:167
#define afw_pool_malloc_type(instance, type, xctx)
Macro to allocate uncleared memory to hold type in pool.
Definition: afw_pool.h:182
afw_utf8_z_printf_v(const afw_utf8_z_t *format_z, va_list ap, const afw_pool_t *p, afw_xctx_t *xctx)
Definition: afw_utf8.c:844
afw_boolean_t afw_utf8_equal(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string equals another string.
afw_utf8_line_column_of_offset(afw_size_t *line_number, afw_size_t *column_number, const afw_utf8_t *s, afw_size_t offset, int tab_size, afw_xctx_t *xctx)
Determine the line and column of an offset in a string.
Definition: afw_utf8.c:1058
afw_utf8_printf(const afw_pool_t *p, afw_xctx_t *xctx, const afw_utf8_z_t *format,...)
Create a utf-8 string using a c format string in specified pool.
Definition: afw_utf8.c:459
afw_utf8_contains(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string contains another string.
Definition: afw_utf8.c:543
afw_value_empty_string
Adaptive value empty string.
Definition: afw_value.h:342
const afw_value_t * afw_value_variable_reference_create(const afw_compile_value_contextual_t *contextual, const afw_value_block_symbol_t *symbol, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for variable reference value.
const afw_value_block_t * afw_value_block_allocated_and_link(const afw_compile_value_contextual_t *contextual, afw_value_compiled_value_t *compiled_value, afw_size_t *block_count, const afw_pool_t *p, afw_xctx_t *xctx)
Create and link a new block.
const afw_value_t * afw_value_qualified_variable_reference_create(const afw_compile_value_contextual_t *contextual, const afw_utf8_t *qualifier, const afw_utf8_t *name, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for variable reference value.
Contextual information provided in some values.
afw_size_t value_size
Size in full_source of value source.
const afw_utf8_t * source_location
Source location.
const afw_value_compiled_value_t * compiled_value
Compiled value this value is part of.
afw_size_t value_offset
Offset in full source of compiled value to this value.
const afw_utf8_t * parser_source
If syntax error, this is partial/full source or NULL.
Definition: afw_error.h:109
NFC normalized UTF-8 string.
Definition: afw_common.h:545
struct for afw_value_block_t
struct for afw_value_block_symbol_t
const afw_utf8_t * full_source_type
The type of the full source.
Interface afw_value public struct.