Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_compile_parse_script.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 Script Parser
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
14 #include "afw_internal.h"
15 
16 
17 
20 static const afw_value_t *
21 impl_function_definition_break =
23 
24 static const afw_value_t *
25 impl_function_definition_continue =
27 
28 
30 afw_compile_parse_list_of_statements(
31  afw_compile_parser_t *parser,
32  afw_boolean_t end_is_close_brace,
33  afw_boolean_t can_be_single_return_expression)
34 {
35  const afw_value_t *result;
36  const afw_value_t *statement;
37  afw_compile_args_t *args;
38  const afw_value_t **argv;
39  const afw_value_block_t *block;
40  afw_size_t argc;
41  afw_size_t start_offset;
42  afw_boolean_t *was_expression;
43  afw_boolean_t was_expression_value;
44 
45  args = afw_compile_args_create(parser);
46  was_expression_value = false;
47  was_expression = (can_be_single_return_expression)
48  ? &was_expression_value
49  : NULL;
50 
51  /* Save starting cursor. */
52  afw_compile_save_cursor(start_offset);
53 
54  /* Make new block and link. */
55  block = afw_compile_parse_link_new_value_block(parser, start_offset);
56 
57  /* Process statements. */
58  for (;;) {
59 
60  afw_compile_get_token();
61  if (end_is_close_brace) {
62  if (afw_compile_token_is(close_bracket) ||
63  afw_compile_token_is(close_brace))
64  {
65  break;
66  }
67  else if afw_compile_token_is(end) {
68  AFW_COMPILE_THROW_ERROR_Z("Expecting '}'");
69  }
70  }
71  else if (afw_compile_token_is(end)) {
72  break;
73  }
74  afw_compile_reuse_token();
75 
76  if (was_expression_value) {
78  "Expression can not be followed by Statement");
79  }
80 
81  statement = afw_compile_parse_Statement(parser, was_expression);
82 
83  if (was_expression_value) {
84  argv = afw_pool_malloc(parser->p,
85  sizeof(afw_value_t *) * 2, parser->xctx);
86  argv[0] = (const afw_value_t *)&afw_function_definition_return;
87  argv[1] = statement;
89  afw_compile_create_contextual_to_cursor(start_offset),
90  1, argv, parser->p, parser->xctx);
91  }
92  was_expression = NULL;
93 
94  if (statement) {
95  afw_compile_args_add_value(args, statement);
96  }
97  }
98 
99  /* Make block of statements. */
100  afw_compile_args_finalize(args, &argc, &argv);
101  afw_value_block_finalize(block, argc, argv, parser->xctx);
102  result = (const afw_value_t *)block;
103 
104  /* Pop block. */
105  afw_compile_parse_pop_value_block(parser);
106 
107  /* Return block or list. */
108  return result;
109 }
110 
111 
112 
113 /*ebnf>>>
114  *
115  *#
116  *# Must evaluate one of following:
117  *# VariableReference
118  *# ReferenceByIndex
119  *# ReferenceByName
120  *
121  * AssignmentExpression ::= Expression
122  *
123  *<<<ebnf*/
125 afw_compile_parse_AssignmentExpression(
126  afw_compile_parser_t *parser)
127 {
128  const afw_value_t *result;
129 
130  result = afw_compile_parse_Expression(parser);
131 
132  return result;
133 }
134 
135 
136 
137 /*ebnf>>>
138  *
139  * OptionalDefineTarget ::=
140  * ( ( 'loc' | 'const' ) AssignmentTarget ) |
141  * AssignmentTarget
142  *
143  *<<<ebnf*/
145 afw_compile_parse_OptionalDefineTarget(
146  afw_compile_parser_t *parser)
147 {
148  const afw_value_t *result;
150 
151  /* Determine assignment type. */
152  assignment_type = afw_compile_assignment_type_assign_only;
153  afw_compile_get_token();
154  if (afw_compile_token_is_name(&afw_s_loc)) {
155  assignment_type = afw_compile_assignment_type_loc;
156  }
157  else if afw_compile_token_is_name(&afw_s_const) {
158  assignment_type = afw_compile_assignment_type_const;
159  }
160  else {
161  afw_compile_reuse_token();
162  }
163 
164  /* Parse AssignmentTarget and return result. */
165  result = afw_compile_parse_AssignmentTarget(parser, assignment_type);
166  return result;
167 }
168 
169 
170 
171 /*ebnf>>>
172  *
173  * OptionalDefineAssignment ::= (
174  * ( ( 'loc' | 'const' ) AssignmentTarget '=' Expression ) |
175  * Assignment
176  * )
177  *
178  *<<<ebnf*/
180 afw_compile_parse_OptionalDefineAssignment(
181  afw_compile_parser_t *parser)
182 {
183  const afw_value_t *result;
185  const afw_value_t **argv;
186  const afw_value_t *function;
187 
188  /* Determine assignment type and function_id. */
189  if (afw_compile_token_is_name(&afw_s_loc)) {
190  assignment_type = afw_compile_assignment_type_loc;
191  function = (const afw_value_t *)&afw_function_definition_loc;
192  }
193  else if afw_compile_token_is_name(&afw_s_const) {
194  assignment_type = afw_compile_assignment_type_const;
195  function = (const afw_value_t *)&afw_function_definition_const;
196  }
197 
198  /* If not const or loc, return result of normal Assignment. */
199  else {
200  return afw_compile_parse_Assignment(parser, NULL);
201  }
202 
203  /* Call appropriate function for const and loc. */
204  argv = afw_pool_malloc(parser->p,
205  sizeof(afw_value_t *) * 4,
206  parser->xctx);
207 
208  argv[0] = function;
209  argv[1] = afw_compile_parse_AssignmentTarget(parser, assignment_type);
210  argv[2] = NULL;
211  argv[3] = NULL;
212 
213  afw_compile_get_token();
214  if (!afw_compile_token_is(equal)) {
215  AFW_COMPILE_THROW_ERROR_Z("Expecting '='");
216  }
217  argv[2] = afw_compile_parse_Expression(parser);
218 
220  afw_compile_create_contextual_to_cursor(
221  parser->token->token_source_offset),
222  3, argv, parser->p, parser->xctx);
223 
224  return result;
225 }
226 
227 
228 
229 /*ebnf>>>
230  *
231  * Assignment ::=
232  * (
233  * AssignmentExpression
234  * ( '=' | '+=' | '-=' | '*=' |'/=' | '%=' | '**=' | '&&=' | '||=' |
235  * '??=' )
236  * Expression
237  * )
238  *
239  *<<<ebnf*/
241 afw_compile_parse_Assignment(
242  afw_compile_parser_t *parser,
243  afw_boolean_t *was_expression)
244 {
245  const afw_value_t *result;
246  const afw_value_t *target;
247  const afw_value_t **argv;
248  const afw_value_t *function;
249  afw_boolean_t just_expression_okay;
250 
251  /* Initialize was_expression to false. */
252  if (was_expression) {
253  *was_expression = false;
254  }
255  just_expression_okay = false;
256 
257  if (afw_compile_token_is(open_bracket) ||
258  afw_compile_token_is(open_brace) ||
259  afw_compile_token_is(open_angle_bracket))
260  {
261  afw_compile_reuse_token();
262  target = afw_compile_parse_AssignmentTarget(parser,
263  afw_compile_assignment_type_assign_only);
264  }
265  else {
266  afw_compile_reuse_token();
267  target = afw_compile_parse_Evaluation(parser);
268  if (!target && was_expression) {
269  target = afw_compile_parse_Expression(parser);
270  }
271  if (!target) {
272  AFW_COMPILE_THROW_ERROR_Z("Invalid assignment target");
273  }
274  if (was_expression) {
275  just_expression_okay = true;
276  }
277  }
278 
279  afw_compile_get_token();
280  switch (parser->token->type) {
281  case afw_compile_token_type_equal:
282  function = NULL;
283  break;
284 
285  case afw_compile_token_type_plus_equal:
286  function = (const afw_value_t *)&afw_function_definition_add;
287  break;
288 
289  case afw_compile_token_type_minus_equal:
290  function = (const afw_value_t *)&afw_function_definition_subtract;
291  break;
292 
293  case afw_compile_token_type_multiply_equal:
294  function = (const afw_value_t *)&afw_function_definition_multiply;
295  break;
296 
297  case afw_compile_token_type_divide_equal:
298  function = (const afw_value_t *)&afw_function_definition_divide;
299  break;
300 
301  case afw_compile_token_type_modulus_equal:
302  function = (const afw_value_t *)&afw_function_definition_mod;
303  break;
304 
305  case afw_compile_token_type_exponentiation_equal:
306  function = (const afw_value_t *)&afw_function_definition_pow;
307  break;
308 
309  case afw_compile_token_type_and_equal:
310  function = (const afw_value_t *)&afw_function_definition_and;
311  break;
312 
313  case afw_compile_token_type_or_equal:
314  function = (const afw_value_t *)&afw_function_definition_or;
315  break;
316 
317  case afw_compile_token_type_nullish_equal:
318  function = (const afw_value_t *)
320  break;
321 
322  default:
323  if (just_expression_okay) {
324  afw_compile_reuse_token();
325  *was_expression = true;
326  return target;
327  }
328  else {
329  AFW_COMPILE_THROW_ERROR_Z("Invalid assignment operator");
330  }
331  }
332 
333  if (function) {
334  result = afw_compile_parse_Expression(parser);
335  argv = afw_pool_malloc(parser->p,
336  sizeof(afw_value_t *) * 3,
337  parser->xctx);
338  argv[0] = function;
339  argv[1] = target;
340  argv[2] = result;
342  afw_compile_create_contextual_to_cursor(
343  parser->token->token_source_offset),
344  2, argv, parser->p, parser->xctx);
345  }
346  else {
347  result = afw_compile_parse_Expression(parser);
348  }
349 
350  argv = afw_pool_malloc(parser->p,
351  sizeof(afw_value_t *) * 3,
352  parser->xctx);
353  argv[0] = (const afw_value_t *)&afw_function_definition_assign;
354  argv[1] = target;
355  argv[2] = result;
357  afw_compile_create_contextual_to_cursor(
358  parser->token->token_source_offset),
359  2, argv, parser->p, parser->xctx);
360 
361  return result;
362 }
363 
364 
365 
366 /*ebnf>>>
367  *
368  * AssignmentStatement ::=
369  * (
370  * ( '(' AssignmentObjectDestructureTarget '=' Expression ')' ) |
371  * Assignment
372  * )
373  * ';'
374  *
375  *<<<ebnf*/
377 afw_compile_parse_AssignmentStatement(
378  afw_compile_parser_t *parser,
379  afw_boolean_t *was_expression)
380 {
381  const afw_value_t *result;
382  afw_boolean_t is_object_destructuring;
383 
384  /*
385  * An object destructure for an assignment statement requires parentheses
386  * around the everything up to statement terminator.
387  */
388  is_object_destructuring = false;
389  if (afw_compile_token_is(open_parenthesis)) {
390  afw_compile_get_token();
391  if afw_compile_token_is(open_brace) {
392  is_object_destructuring = true;
393  }
394  else {
395  afw_compile_reuse_token();
396  }
397  }
398 
399  result = afw_compile_parse_Assignment(parser,
400  is_object_destructuring ? NULL : was_expression);
401 
402  /* Check for close parenthesis if object destructuring. */
403  if (is_object_destructuring) {
404  afw_compile_get_token();
405  if (!afw_compile_token_is(close_parenthesis)) {
406  AFW_COMPILE_THROW_ERROR_Z("Expecting ')'");
407  }
408  }
409 
410  if (!was_expression || !*was_expression) {
411  AFW_COMPILE_ASSERT_NEXT_TOKEN_IS_SEMICOLON;
412  }
413 
414  return result;
415 }
416 
417 
418 
419 /*ebnf>>>
420  *
421  * BreakStatement ::= 'break' ';'
422  *
423  *<<<ebnf*/
424 static const afw_value_t *
425 impl_parse_BreakStatement(afw_compile_parser_t *parser)
426 {
427  const afw_value_t *result;
428 
429  /*FIXME Check for inside loop. */
430 
431  result = afw_value_call_create(
432  afw_compile_create_contextual_to_cursor(
433  parser->token->token_source_offset),
434  0, &impl_function_definition_break, parser->p, parser->xctx);
435 
436  AFW_COMPILE_ASSERT_NEXT_TOKEN_IS_SEMICOLON;
437 
438  return result;
439 }
440 
441 
442 
443 /*ebnf>>>
444  *
445  * ConstStatement ::= 'const' AssignmentTarget '=' Expression ';'
446  *
447  *<<<ebnf*/
448 static const afw_value_t *
449 impl_parse_ConstStatement(afw_compile_parser_t *parser)
450 {
451  const afw_value_t *result;
452  const afw_value_t **argv;
453 
454  result = NULL;
455  argv = afw_pool_malloc(parser->p,
456  sizeof(afw_value_t *) * 4,
457  parser->xctx);
458 
459  argv[0] = (const afw_value_t *)&afw_function_definition_const;
460  argv[1] = afw_compile_parse_AssignmentTarget(parser,
461  afw_compile_assignment_type_const);
462  argv[2] = NULL;
463  argv[3] = NULL;
464 
465  afw_compile_get_token();
466  if (!afw_compile_token_is(equal)) {
467  AFW_COMPILE_THROW_ERROR_Z("Expecting '='");
468  }
469  argv[2] = afw_compile_parse_Expression(parser);
470  AFW_COMPILE_ASSERT_NEXT_TOKEN_IS_SEMICOLON;
471 
473  afw_compile_create_contextual_to_cursor(
474  parser->token->token_source_offset),
475  3, argv, parser->p, parser->xctx);
476 
477  return result;
478 }
479 
480 
481 
482 /*ebnf>>>
483  *
484  * InterfaceName ::= Identifier
485  *
486  * InterfaceStatement ::= 'interface' InterfaceName
487  * '{'
488  * ( String | Identifier ) ':' Type
489  * ( ',' ( String | Identifier ) ':' Type )*
490  * ','?
491  * '}' ';'
492  *
493  *<<<ebnf*/
494 static const afw_value_t *
495 impl_parse_InterfaceStatement(afw_compile_parser_t *parser)
496 {
497  const afw_value_t *result;
498 
499  AFW_COMPILE_THROW_ERROR_Z("interface statement is not supported yet");
500 
501  return result;
502 }
503 
504 
505 
506 /*ebnf>>>
507  *
508  * TypeVariableName ::= Identifier
509  *
510  * TypeStatement ::= 'type' TypeVariableName '=' Type ';'
511  *
512  *<<<ebnf*/
513 static const afw_value_t *
514 impl_parse_TypeStatement(afw_compile_parser_t *parser)
515 {
516  const afw_value_t *result;
517 
518  AFW_COMPILE_THROW_ERROR_Z("type statement is not supported yet");
519 
520  return result;
521 }
522 
523 
524 
525 /*ebnf>>>
526  *
527  * ContinueStatement ::= 'continue' ';'
528  *
529  *<<<ebnf*/
530 static const afw_value_t *
531 impl_parse_ContinueStatement(afw_compile_parser_t *parser)
532 {
533  const afw_value_t *result;
534 
535  /*FIXME Check for inside loop. */
536 
537  result = afw_value_call_create(
538  afw_compile_create_contextual_to_cursor(
539  parser->token->token_source_offset),
540  0, &impl_function_definition_continue, parser->p, parser->xctx);
541 
542  AFW_COMPILE_ASSERT_NEXT_TOKEN_IS_SEMICOLON;
543 
544  return result;
545 }
546 
547 
548 /*ebnf>>>
549  *
550  * DeclareStatement ::= 'declare' AssignmentTarget ';'
551  *
552  *<<<ebnf*/
553 static const afw_value_t *
554 impl_parse_DeclareStatement(afw_compile_parser_t *parser)
555 {
556  AFW_COMPILE_THROW_ERROR_Z("Not implemented");
557 }
558 
559 
560 
561 /*ebnf>>>
562  *
563  * DoWhileStatement ::= 'do' Statement 'while' '(' Expression ')' ';'
564  *
565  *<<<ebnf*/
566 static const afw_value_t *
567 impl_parse_DoWhileStatement(afw_compile_parser_t *parser)
568 {
569  const afw_value_t *result;
570  const afw_value_t **argv;
571  afw_size_t start_offset;
572 
573  afw_compile_save_cursor(start_offset);
574 
575  argv = afw_pool_malloc(parser->p, sizeof(afw_value_t *) * 3, parser->xctx);
576  argv[0] = (const afw_value_t *)&afw_function_definition_do_while;
577  argv[2] = afw_compile_parse_Statement(parser, NULL);
578 
579  afw_compile_get_token();
580  if (!afw_compile_token_is_name(&afw_s_while)) {
582  "Expecting while");
583  }
584 
585  /* ( expression ) */
586  afw_compile_get_token();
587  if (!afw_compile_token_is(open_parenthesis)) {
588  AFW_COMPILE_THROW_ERROR_Z("Expecting '('");
589  }
590  argv[1] = afw_compile_parse_Expression(parser);
591  afw_compile_get_token();
592  if (!afw_compile_token_is(close_parenthesis)) {
593  AFW_COMPILE_THROW_ERROR_Z("Expecting ')'");
594  }
595 
596  AFW_COMPILE_ASSERT_NEXT_TOKEN_IS_SEMICOLON;
597 
599  afw_compile_create_contextual_to_cursor(start_offset),
600  2, argv, parser->p, parser->xctx);
601 
602  return result;
603 }
604 
605 
606 
607 /*ebnf>>>
608  *
609  * ForStatement ::= 'for'
610  * '('
611  * ( OptionalDefineAssignment ( ',' OptionalDefineAssignment )* )?
612  * ';' Expression?
613  * ';' ( Assignment ( ',' Assignment )* )?
614  * ')' Statement
615  *
616  *<<<ebnf*/
617 static const afw_value_t *
618 impl_parse_ForStatement(afw_compile_parser_t *parser)
619 {
620  const afw_value_t *result;
621  const afw_value_t **argv;
622  const afw_value_t *value;
623  const afw_list_t *list;
624  const afw_value_block_t *block;
625  afw_size_t start_offset;
626 
627  block = NULL;
628  afw_compile_save_cursor(start_offset);
629 
630  argv = afw_pool_malloc(parser->p, sizeof(afw_value_t *) * 5, parser->xctx);
631  argv[0] = (const afw_value_t *)&afw_function_definition_for;
632 
633  afw_compile_get_token();
634  if (!afw_compile_token_is(open_parenthesis)) {
635  AFW_COMPILE_THROW_ERROR_Z("Expecting '('");
636  }
637 
638  /* ( OptionalDefineAssignment ( ',' OptionalDefineAssignment )* )? ';' */
639  afw_compile_get_token();
640  list = NULL;
641  if (!afw_compile_token_is(semicolon)) {
642  for (;;) {
643 
644  /* If there are loc and/or const, a block is needed. */
645  if (afw_compile_token_is_name(&afw_s_loc) ||
646  afw_compile_token_is_name(&afw_s_const))
647  {
648  if (!block) {
649  block = afw_compile_parse_link_new_value_block(
650  parser, start_offset);
651  }
652  value = afw_compile_parse_OptionalDefineAssignment(parser);
653  }
654  else {
655  value = afw_compile_parse_Assignment(parser, NULL);
656  }
657  if (!list) {
658  list = afw_list_create_generic(parser->p, parser->xctx);
659  }
660  afw_list_add_value(list, value, parser->xctx);
661  afw_compile_get_token();
662  if (afw_compile_token_is(semicolon)) {
663  break;
664  }
665  if (!afw_compile_token_is(comma)) {
666  AFW_COMPILE_THROW_ERROR_Z("Expecting ',' or ';'");
667  }
668  afw_compile_get_token();
669  }
670  }
671  argv[1] = NULL;
672  if (list) {
673  argv[1] = afw_value_create_list(list, parser->p, parser->xctx);
674  }
675 
676  /* Expression? ';' */
677  argv[2] = NULL;
678  afw_compile_get_token();
679  if (!afw_compile_token_is(semicolon)) {
680  afw_compile_reuse_token();
681  argv[2] = afw_compile_parse_Expression(parser);
682  afw_compile_get_token();
683  if (!afw_compile_token_is(semicolon)) {
684  AFW_COMPILE_THROW_ERROR_Z("Expecting ';'");
685  }
686  }
687 
688  /* ( Assignment ( ',' Assignment )* )? ')' */
689  afw_compile_get_token();
690  list = NULL;
691  if (!afw_compile_token_is(close_parenthesis)) {
692  for (;;) {
693  value = afw_compile_parse_Assignment(parser, NULL);
694  if (!list) {
695  list = afw_list_create_generic(parser->p, parser->xctx);
696  }
697  afw_list_add_value(list, value, parser->xctx);
698  afw_compile_get_token();
699  if (afw_compile_token_is(close_parenthesis)) {
700  break;
701  }
702  if (!afw_compile_token_is(comma)) {
703  AFW_COMPILE_THROW_ERROR_Z("Expecting ',' or ')'");
704  }
705  afw_compile_get_token();
706  }
707  }
708  argv[3] = NULL;
709  if (list) {
710  argv[3] = afw_value_create_list(list, parser->p, parser->xctx);
711  }
712 
713  argv[4] = afw_compile_parse_Statement(parser, NULL);
714 
716  afw_compile_create_contextual_to_cursor(start_offset),
717  4, argv, parser->p, parser->xctx);
718 
719  if (block) {
720  argv = afw_pool_malloc(parser->p, sizeof(afw_value_t *), parser->xctx);
721  argv[0] = result;
722  afw_value_block_finalize(block, 1, argv, parser->xctx);
723  result = (const afw_value_t *)block;
724  }
725 
726  return result;
727 }
728 
729 
730 
731 /*ebnf>>>
732  *
733  *# This will probably change when set operators are added.
734  *
735  * ForeachStatement ::=
736  * 'foreach' OptionalDefineTarget 'of' Expression Statement
737  *
738  *<<<ebnf*/
739 static const afw_value_t *
740 impl_parse_ForeachStatement(afw_compile_parser_t *parser)
741 {
742  const afw_value_t *result;
743  const afw_value_t **argv;
744  afw_size_t start_offset;
745 
746  afw_compile_save_cursor(start_offset);
747 
748  argv = afw_pool_malloc(parser->p, sizeof(afw_value_t *) * 4, parser->xctx);
749  argv[0] = (const afw_value_t *)&afw_function_definition_foreach;
750  argv[1] = afw_compile_parse_OptionalDefineTarget(parser);
751 
752  afw_compile_get_token();
753  if (!afw_compile_token_is_name(&afw_s_of))
754  {
755  AFW_COMPILE_THROW_ERROR_Z("Expecting 'of'");
756  }
757 
758  argv[2] = afw_compile_parse_Expression(parser);
759  argv[3] = afw_compile_parse_Statement(parser, NULL);
760 
762  afw_compile_create_contextual_to_cursor(start_offset),
763  3, argv, parser->p, parser->xctx);
764 
765  return result;
766 }
767 
768 
769 
770 /*ebnf>>>
771  *# Named function
772  *#
773  *# FunctionName must be present for this production to match.
774  *
775  * FunctionStatement ::=
776  * 'function' ( FunctionName - ReservedWords ) FunctionSignatureAndBody ';'
777  *
778  *<<<ebnf*/
779 static const afw_value_t *
780 impl_parse_FunctionStatement(afw_compile_parser_t *parser)
781 {
782  const afw_value_t *result;
783  const afw_value_t **argv;
784  afw_size_t start_offset;
785 
786  afw_compile_save_cursor(start_offset);
787  afw_compile_get_token();
788  if (!afw_compile_token_is_unqualified_identifier()) {
789  afw_compile_reuse_token();
790  return NULL;
791  }
792 
793  if (afw_compile_is_reserved_word(parser, parser->token->identifier_name))
794  {
795  AFW_COMPILE_THROW_ERROR_Z("Function name can not be a reserved word");
796  }
797 
798  afw_compile_parse_add_symbol_entry(parser,
799  parser->token->identifier_name);
800 
801  argv = afw_pool_malloc(parser->p, sizeof(afw_value_t *) * 4, parser->xctx);
802  argv[0] = (const afw_value_t *)&afw_function_definition_const;
803  argv[1] = afw_value_create_string(parser->token->identifier_name,
804  parser->p, parser->xctx);
805  argv[2] = afw_compile_parse_FunctionSignatureAndBody(parser);
806  argv[3] = NULL;
807 
809  afw_compile_create_contextual_to_cursor(start_offset),
810  3, argv, parser->p, parser->xctx);
811 
812  return result;
813 }
814 
815 
816 
817 /*ebnf>>>
818  *
819  * IfStatement ::= 'if' '(' Expression ')' Statement ( 'else' Statement )?
820  *
821  *<<<ebnf*/
822 static const afw_value_t *
823 impl_parse_IfStatement(afw_compile_parser_t *parser)
824 {
825  const afw_value_t *result;
826  const afw_value_t *condition;
827  const afw_value_t *then;
828  const afw_value_t *otherwise;
829  const afw_value_t **argv;
830  afw_size_t argc;
831  afw_size_t start_offset;
832 
833  afw_compile_save_cursor(start_offset);
834 
835  /* ( expression ) */
836  afw_compile_get_token();
837  if (!afw_compile_token_is(open_parenthesis)) {
838  AFW_COMPILE_THROW_ERROR_Z("Expecting '('");
839  }
840  condition = afw_compile_parse_Expression(parser);
841  afw_compile_get_token();
842  if (!afw_compile_token_is(close_parenthesis)) {
843  AFW_COMPILE_THROW_ERROR_Z("Expecting ')'");
844  }
845 
846  /* statement and optional else statement. */
847  then = afw_compile_parse_Statement(parser, NULL);
848  otherwise = NULL;
849 
850  afw_compile_get_token();
851  if (afw_compile_token_is_unqualified_identifier()) {
852  if (afw_utf8_equal(parser->token->identifier_name, &afw_s_else))
853  {
854  otherwise = afw_compile_parse_Statement(parser, NULL);
855  }
856  else {
857  afw_compile_reuse_token();
858  }
859  }
860  else {
861  afw_compile_reuse_token();
862  }
863 
864  /* Create call value for if and return it. */
865  argc = otherwise ? 3 : 2;
866  argv = afw_pool_malloc(parser->p,
867  sizeof(afw_value_t *) * (argc + 1),
868  parser->xctx);
869  argv[0] = (const afw_value_t *)&afw_function_definition_if;
870  argv[1] = condition;
871  argv[2] = then;
872  if (otherwise) {
873  argv[3] = otherwise;
874  }
876  afw_compile_create_contextual_to_cursor(start_offset),
877  argc, argv, parser->p, parser->xctx);
878  return result;
879 }
880 
881 
882 
883 /*ebnf>>>
884  *
885  * LocStatement ::= 'loc' AssignmentTarget ( '=' Expression )? ';'
886  *
887  *<<<ebnf*/
888 static const afw_value_t *
889 impl_parse_LocStatement(afw_compile_parser_t *parser)
890 {
891  const afw_value_t *result;
892  const afw_value_t **argv;
893 
894  result = NULL;
895  argv = afw_pool_malloc(parser->p,
896  sizeof(afw_value_t *) * 4,
897  parser->xctx);
898 
899  argv[0] = (const afw_value_t *)&afw_function_definition_loc;
900  argv[1] = afw_compile_parse_AssignmentTarget(parser,
901  afw_compile_assignment_type_loc);
902  argv[2] = NULL;
903  argv[3] = NULL;
904 
905  afw_compile_get_token();
906  if (!afw_compile_token_is(semicolon)) {
907  if (!afw_compile_token_is(equal)) {
908  AFW_COMPILE_THROW_ERROR_Z("Expecting '='");
909  }
910  argv[2] = afw_compile_parse_Expression(parser);
911  AFW_COMPILE_ASSERT_NEXT_TOKEN_IS_SEMICOLON;
912  }
913 
915  afw_compile_create_contextual_to_cursor(
916  parser->token->token_source_offset),
917  3, argv, parser->p, parser->xctx);
918 
919  return result;
920 }
921 
922 
923 /*ebnf>>>
924  *
925  *# Expression is required if inside a function that has a non-void return.
926  * ReturnStatement ::= 'return' Expression? ';'
927  *
928  *<<<ebnf*/
929 static const afw_value_t *
930 impl_parse_ReturnStatement(afw_compile_parser_t *parser)
931 {
932  const afw_value_t *result;
933  const afw_value_t **argv;
934  afw_size_t start_offset;
935 
936  afw_compile_save_cursor(start_offset);
937 
938  argv = afw_pool_malloc(parser->p, sizeof(afw_value_t *) * 2, parser->xctx);
939  argv[0] = (const afw_value_t *)&afw_function_definition_return;
940  argv[1] = afw_value_null;
941 
942  afw_compile_get_token();
943  if (!afw_compile_token_is(semicolon)) {
944  afw_compile_reuse_token();
945  argv[1] = afw_compile_parse_Expression(parser);
946  AFW_COMPILE_ASSERT_NEXT_TOKEN_IS_SEMICOLON;
947  }
948 
950  afw_compile_create_contextual_to_cursor(start_offset),
951  1, argv, parser->p, parser->xctx);
952 
953  return result;
954 }
955 
956 
957 /*ebnf>>>
958  *
959  * WhileStatement ::= 'while' '(' Expression ')' Statement
960  *
961  *<<<ebnf*/
962 static const afw_value_t *
963 impl_parse_WhileStatement(afw_compile_parser_t *parser)
964 {
965  const afw_value_t *result;
966  const afw_value_t **argv;
967  afw_size_t start_offset;
968 
969  afw_compile_save_cursor(start_offset);
970 
971  argv = afw_pool_malloc(parser->p, sizeof(afw_value_t *) * 3, parser->xctx);
972  argv[0] = (const afw_value_t *)&afw_function_definition_while;
973 
974  /* ( expression ) */
975  afw_compile_get_token();
976  if (!afw_compile_token_is(open_parenthesis)) {
977  AFW_COMPILE_THROW_ERROR_Z("Expecting '('");
978  }
979  argv[1] = afw_compile_parse_Expression(parser);
980  afw_compile_get_token();
981  if (!afw_compile_token_is(close_parenthesis)) {
982  AFW_COMPILE_THROW_ERROR_Z("Expecting ')'");
983  }
984 
985  argv[2] = afw_compile_parse_Statement(parser, NULL);
986 
988  afw_compile_create_contextual_to_cursor(start_offset),
989  2, argv, parser->p, parser->xctx);
990 
991  return result;
992 }
993 
994 
995 
996 /*ebnf>>>
997  *
998  *# This is any Evaluation that compiles to a call adaptive value.
999  * EvaluationThatCompilesToCallValue ::= Evaluation
1000  *
1001  * CallStatement ::= EvaluationThatCompilesToCallValue
1002  *
1003  *# BreakStatement and ContinueStatement can only be in a loop
1004  *
1005  * Block ::= '{' Statement* '}'
1006  *
1007  * Statement ::=
1008  * ';' |
1009  * Block |
1010  * AssignmentStatement |
1011  * BreakStatement |
1012  * CallStatement |
1013  * ConstStatement |
1014  * ContinueStatement |
1015  * DeclareStatement |
1016  * DoWhileStatement |
1017  * ForStatement |
1018  * ForeachStatement |
1019  * FunctionStatement |
1020  * IfStatement |
1021  * InterfaceStatement |
1022  * LocStatement |
1023  * ReturnStatement |
1024  * TypeStatement |
1025  * WhileStatement
1026  *
1027  *<<<ebnf*/
1029 afw_compile_parse_Statement(
1030  afw_compile_parser_t *parser,
1031  afw_boolean_t *was_expression)
1032 {
1033  const afw_value_t *result;
1034  afw_boolean_t was_assignment_expression;
1035 
1036  /* Initialize was_expression to false. */
1037  if (was_expression) {
1038  *was_expression = false;
1039  }
1040 
1041  /* Get next token. */
1042  afw_compile_get_token();
1043 
1044  /* Get token '{'. */
1045  if (afw_compile_token_is(open_brace)) {
1046  result = afw_compile_parse_list_of_statements(parser, true, false);
1047  return result;
1048  }
1049 
1050  /* If not assignment, process statement. */
1051  result = NULL;
1052  if (afw_compile_token_is(identifier) &&
1053  !parser->token->identifier_qualifier)
1054  {
1055  if (afw_utf8_equal(parser->token->identifier_name,
1056  &afw_s_loc))
1057  {
1058  result = impl_parse_LocStatement(parser);
1059  }
1060  else if (afw_utf8_equal(parser->token->identifier_name,
1061  &afw_s_const))
1062  {
1063  result = impl_parse_ConstStatement(parser);
1064  }
1065  else if (afw_utf8_equal(parser->token->identifier_name,
1066  &afw_s_break))
1067  {
1068  result = impl_parse_BreakStatement(parser);
1069  }
1070  else if (afw_utf8_equal(parser->token->identifier_name,
1071  &afw_s_continue))
1072  {
1073  result = impl_parse_ContinueStatement(parser);
1074  }
1075  else if (afw_utf8_equal(parser->token->identifier_name,
1076  &afw_s_do))
1077  {
1078  result = impl_parse_DoWhileStatement(parser);
1079  }
1080  else if (afw_utf8_equal(parser->token->identifier_name,
1081  &afw_s_for))
1082  {
1083  result = impl_parse_ForStatement(parser);
1084  }
1085  else if (afw_utf8_equal(parser->token->identifier_name,
1086  &afw_s_foreach))
1087  {
1088  result = impl_parse_ForeachStatement(parser);
1089  }
1090  else if (afw_utf8_equal(parser->token->identifier_name,
1091  &afw_s_if))
1092  {
1093  result = impl_parse_IfStatement(parser);
1094  }
1095  else if (afw_utf8_equal(parser->token->identifier_name,
1096  &afw_s_return))
1097  {
1098  result = impl_parse_ReturnStatement(parser);
1099  }
1100  else if (afw_utf8_equal(parser->token->identifier_name,
1101  &afw_s_while))
1102  {
1103  result = impl_parse_WhileStatement(parser);
1104  }
1105  else if (afw_utf8_equal(parser->token->identifier_name,
1106  &afw_s_function))
1107  {
1108  result = impl_parse_FunctionStatement(parser);
1109  }
1110  else if (afw_utf8_equal(parser->token->identifier_name,
1111  &afw_s_interface))
1112  {
1113  result = impl_parse_InterfaceStatement(parser);
1114  }
1115  else if (afw_utf8_equal(parser->token->identifier_name,
1116  &afw_s_type))
1117  {
1118  result = impl_parse_TypeStatement(parser);
1119  }
1120  else if (afw_utf8_equal(parser->token->identifier_name,
1121  &afw_s_declare))
1122  {
1123  result = impl_parse_DeclareStatement(parser);
1124  }
1125  }
1126 
1127  /*
1128  * If there is not a result already and this is not an empty statement, see
1129  * if this is a call or assignment statement by calling
1130  * afw_compile_parse_AssignmentStatement() with a was_expression parameter
1131  * to get a value. The result is the first applicable of the following
1132  * based on this value:
1133  *
1134  * 1) If the value is not an expression, the result is an assignment
1135  * statement.
1136  *
1137  * 2) If the value is a call value followed by a semicolon, the result is a
1138  * call statement.
1139  *
1140  * 3) If the was_expression parameter is not NULL on the call to
1141  * afw_compile_parse_Statement(), it's value is set to true and the
1142  * expression is returned.
1143  *
1144  * 4) An invalid statement error is thrown.
1145  *
1146  */
1147  if (!result) {
1148  if (afw_compile_token_is(semicolon)) {
1149  return NULL;
1150  }
1151  was_assignment_expression = false;
1152  result = afw_compile_parse_AssignmentStatement(parser,
1153  &was_assignment_expression);
1154  if (was_assignment_expression)
1155  {
1156  if (afw_value_is_any_call(result)) {
1157  afw_compile_get_token();
1158  if (afw_compile_token_is(semicolon)) {
1159  was_assignment_expression = false;
1160  }
1161  else {
1162  afw_compile_reuse_token();
1163  }
1164  }
1165  }
1166  if (was_assignment_expression) {
1167  if (was_expression) {
1168  *was_expression = was_assignment_expression;
1169  }
1170  else {
1171  result = NULL;
1172  }
1173  }
1174  }
1175 
1176  /* If no result yet, throw error. */
1177  if (!result) {
1178  AFW_COMPILE_THROW_ERROR_Z("Invalid statement");
1179  }
1180 
1181  return result;
1182 }
1183 
1184 
1185 
1186 /*ebnf>>>
1187  *
1188  *#
1189  *# The value returned from a script if the Expression specified on an
1190  *# evaluated ReturnStatement, the single Expression specified, or null
1191  *# if none of the above.
1192  *#
1193  * Script ::= ScriptShebang? ( Statement* | Expression )
1194  *
1195  * ScriptShebang ::= '#!' UnicodeNonControl* 'afw' UnicodeNonControl* '\n'
1196  *
1197  *<<<ebnf*/
1199 afw_compile_parse_Script(
1200  afw_compile_parser_t *parser)
1201 {
1202  const afw_value_t *result;
1203  afw_utf8_t line;
1204 
1205  /*
1206  * Shebang line must contain afw. If it also contains test_script, parse
1207  * using TestScript production.
1208  */
1209  if (afw_compile_next_raw_starts_with_z("#!")) {
1210  afw_compile_get_raw_line(&line);
1211  if (!afw_utf8_contains(&line, &afw_s_afw) &&
1212  !afw_utf8_contains(&line, &afw_s_maluba)) /* Easter egg */
1213  {
1215  "Shebang line must contain afw to be recognized as an "
1216  "adaptive script in a hybrid value");
1217  }
1218  if (afw_utf8_contains(&line, &afw_s_a_dash_s_test_script) ||
1219  afw_utf8_contains(&line, &afw_s_a_dash_dash_syntax_test_script))
1220  {
1221  return afw_compile_parse_TestScript(parser);
1222  }
1223  }
1224 
1225  /* Parse statements and return. */
1226  result = afw_compile_parse_list_of_statements(parser, false, true);
1227  return result;
1228 }
1229 
1230 
1231 static void
1232 impl_test_script_get_next_key_value(
1233  afw_compile_parser_t *parser,
1234  const afw_utf8_t **key,
1235  const afw_utf8_t **string,
1236  afw_size_t *string_offset,
1237  afw_size_t *string_length)
1238 {
1239  afw_utf8_t line;
1240  afw_utf8_t remaining;
1241  const afw_utf8_octet_t *c;
1242  const afw_utf8_octet_t *end;
1243  const afw_utf8_octet_t *start;
1244  afw_size_t start_cursor, end_cursor;
1245  int state;
1246 
1247  afw_compile_save_cursor(*string_offset);
1248 
1249  for (*key = NULL, *string = NULL;;) {
1250 
1251  afw_compile_get_raw_line(&line);
1252  if (afw_compile_is_at_eof()) {
1253  break;
1254  }
1255 
1256  if (!afw_utf8_starts_with_z(&line, "//?")) {
1257  AFW_COMPILE_THROW_ERROR_Z("Line must start with //?");
1258  }
1259 
1260  for (
1261  state = 0,
1262  c = line.s + 3,
1263  start = line.s,
1264  end = line.s + line.len;
1265  ;
1266  c++)
1267  {
1268  switch (state) {
1269 
1270  /* Find begin of key */
1271  case 0:
1272  if (c >= end) {
1273  break;
1274  }
1275  if (*c != ' ') {
1276  start = c;
1277  state = 1;
1278  }
1279  break;
1280 
1281  /* Find end of key */
1282  case 1:
1283  if (c >= end) {
1284  AFW_COMPILE_THROW_ERROR_Z("Expecting ':' after key");
1285  }
1286  if (*c == ':' || *c == ' ') {
1287  *key = afw_utf8_create_copy(start, c - start,
1288  parser->p, parser->xctx);
1289  state = 2;
1290  if (*c == ':') {
1291  state=3;
1292  }
1293  }
1294  break;
1295 
1296  /* Find ':' */
1297  case 2:
1298  if (c >= end) {
1299  AFW_COMPILE_THROW_ERROR_Z("Expecting ':' after key");
1300  }
1301  if (*c == ':') {
1302  state = 3;
1303  }
1304  else if (*c != ' ') {
1305  AFW_COMPILE_THROW_ERROR_Z("Expecting ':' after key");
1306  }
1307  break;
1308 
1309  /* Get string. */
1310  case 3:
1311 
1312  /* If end of line, string is NULL. */
1313  if (c >= end) {
1314  break;
1315  }
1316 
1317  /* If start of string found, extract it. */
1318  if (*c != ' ') {
1319  start = c;
1320  remaining.s = start;
1321  remaining.len = end - start;
1322 
1323  /* If "...", string is next line all all to end or "//?". */
1324  if (afw_utf8_starts_with_z(&remaining, "...")) {
1325  afw_compile_save_cursor(start_cursor);
1326  afw_compile_save_cursor(*string_offset);
1327  for (;;) {
1328  afw_compile_save_cursor(end_cursor);
1329  afw_compile_get_raw_line(&line);
1330  if (!line.s) {
1331  *string_length = end_cursor - start_cursor;
1332  *string = afw_utf8_create_copy(
1333  parser->full_source->s + start_cursor,
1334  *string_length, parser->p, parser->xctx);
1335  break;
1336  }
1337  else if (afw_utf8_starts_with_z(&line, "//?")) {
1338  afw_compile_restore_cursor(end_cursor);
1339  /* Don't include \n before //? */
1340  *string_length = end_cursor - start_cursor - 1;
1341  *string = afw_utf8_create_copy(
1342  parser->full_source->s + start_cursor,
1343  *string_length, parser->p, parser->xctx);
1344  break;
1345  }
1346  }
1347  c = end;
1348  }
1349 
1350  /*
1351  * If not "..." string is rest of line except for training
1352  * whitespace.
1353  */
1354  else {
1355  *string_offset = start - parser->full_source->s;
1356  for (c = end - 1; c > start && *c == ' '; c--);
1357  *string_length = c - start + 1;
1358  *string = afw_utf8_create_copy(start, *string_length,
1359  parser->p, parser->xctx);
1360  c = end;
1361  }
1362  }
1363  break;
1364  }
1365 
1366  /* If at end of line, break. */
1367  if (c >= end) {
1368  break;
1369  }
1370  }
1371 
1372  /* If key found, finished. */
1373  if (*key) {
1374  break;
1375  }
1376  }
1377 
1378  if (afw_utf8_equal(*key, &afw_s_expect)) {
1379  if (afw_utf8_starts_with(*string, &afw_s_error) &&
1380  (*string)->len > afw_s_error.len &&
1381  *((*string)->s + afw_s_error.len) != ':')
1382  {
1384  "Must be \"error\" by itself or \"error:\" immediately "
1385  "followed by the exact error message expected");
1386  }
1387  }
1388 }
1389 
1390 
1391 /*ebnf>>>
1392  *
1393  * TestScript ::= TestScriptShebang? TestScriptDefinition TestDefinition+
1394  *
1395  * TestScriptShebang ::= '#!' UnicodeNonControl* 'afw' UnicodeNonControl*
1396  * ('-s test_script' | '--syntax test_script' ) | UnicodeNonControl* '\n'
1397  *
1398  *# TestDescription, TestSkip, TestSourceType and TestCustomProperty can occur
1399  *# in any order
1400  * TestScriptDefinition ::=
1401  * TestScriptBegin
1402  * TestDescription?
1403  * TestSkip?
1404  * TestSourceType?
1405  * TestCustomProperty*
1406  *
1407  *# TestDescription, TestExpect, TestSkip, TestSource, TestSourceType and
1408  *# TestUserProperty can occur in any order
1409  * TestDefinition ::=
1410  * TestBegin
1411  * TestDescription?
1412  * TestExpect
1413  * TestSkip?
1414  * TestSource
1415  * TestSourceType?
1416  * TestCustomProperty*
1417  *
1418  *# Must start at the beginning of a line
1419  * TestScriptLineStart ::= ( '//?' '\n')* '//?'
1420  *
1421  *# The '//?' of '\n//?' is reused. The last '/n' is ignored to allow ...
1422  *# values to not end with a '\n'.
1423  * TestScriptValue ::= (
1424  * ( '...' '\n' UnicodeCodePoint* ( '\n//?' | END ) ) |
1425  * ( UnicodeNonControl #x20* '\n' )
1426  * )
1427  *
1428  * TestScriptBegin ::= TestScriptLineStart 'test_script:' TestScriptValue
1429  *
1430  * TestBegin ::= TestScriptLineStart 'test:' TestScriptValue
1431  *
1432  *# Identifier should be one that will not collide with standard ones.
1433  * TestCustomProperty ::= Identifier ':' TestScriptValue
1434  *
1435  * TestDescription ::= TestScriptLineStart 'description:' TestScriptValue
1436  *
1437  * TestExpect ::= TestScriptLineStart
1438  * 'expect:' (
1439  * ( 'error' '\n' ) |
1440  * ( 'result' TestScriptValue )
1441  * )
1442  *
1443  *# Skip can be use for source that is not ready to test
1444  * TestSkip ::= TestScriptLineStart 'skip:' 'true' '\n'
1445  *
1446  *# Default is script or the one specified in TestScriptDefinition
1447  * TestSourceType ::= TestScriptLineStart 'source_type:'
1448  * ( 'expression' | 'expression_tuple' | 'hybrid' | 'json' |
1449  * 'parenthesized_expression' | 'relaxed_json' | 'script' |
1450  * 'template' )
1451  *
1452  * TestSource ::= TestScriptLineStart 'source:' TestScriptValue
1453  *
1454  *<<<ebnf*/
1456 afw_compile_parse_TestScript(
1457  afw_compile_parser_t *parser)
1458 {
1459  const afw_value_t *result;
1460  const afw_object_t *test_script_object;
1461  const afw_list_t *test_list;
1462  const afw_object_t *test_object;
1463  const afw_utf8_t *test_script_id;
1464  const afw_utf8_t *key;
1465  const afw_utf8_t *string;
1466  const afw_utf8_t *global_source_type;
1467  const afw_value_t **argv;
1468  const afw_utf8_t *source_location;
1469  const afw_utf8_t *expect_location;
1470 
1471  afw_size_t source_line;
1472  afw_size_t source_column;
1473 
1474  afw_size_t start_offset;
1475 
1476  afw_size_t string_offset;
1477  afw_size_t string_length;
1478 
1479  afw_utf8_t line;
1480 
1481  /* Save starting cursor. */
1482  afw_compile_save_cursor(start_offset);
1483 
1484  /* Shebang line must contain afw and -s test_script. */
1485  if (afw_compile_next_raw_starts_with_z("#!")) {
1486  afw_compile_get_raw_line(&line);
1487  if ((!afw_utf8_contains(&line, &afw_s_afw) &&
1488  !afw_utf8_contains(&line, &afw_s_maluba)) /* Easter egg */ ||
1489  (!afw_utf8_contains(&line, &afw_s_a_dash_s_test_script) &&
1490  !afw_utf8_contains(&line, &afw_s_a_dash_dash_syntax_test_script)))
1491  {
1493  "Shebang line must contain afw and -s[yntax] test_script to "
1494  "be recognized as an adaptive test script");
1495  }
1496  }
1497 
1498  test_script_object = afw_object_create(parser->p, parser->xctx);
1500  parser->p, parser->xctx);
1501  afw_object_set_property_as_list(test_script_object,
1502  &afw_s_tests, test_list, parser->xctx);
1503 
1504  /* Process TestScriptDefinition */
1505  for (global_source_type = NULL, test_script_id = NULL;;)
1506  {
1507  impl_test_script_get_next_key_value(parser,
1508  &key, &string, &string_offset, &string_length);
1509  if (afw_utf8_equal(key, &afw_s_testScript)) {
1510  test_script_id = string;
1511  }
1512  else if (!test_script_id) {
1514  "test_script: must be specified first");
1515  }
1516  if (!key || afw_utf8_equal(key, &afw_s_test)) {
1517  break;
1518  }
1519  if (afw_utf8_equal(key, &afw_s_skip)) {
1520  if (afw_utf8_equal(string, &afw_s_true)) {
1521  afw_object_set_property(test_script_object,
1522  key, afw_value_true, parser->xctx);
1523  }
1524  else if (!afw_utf8_equal(string, &afw_s_false)) {
1526  "skip: must be true or false");
1527  }
1528  }
1529  else {
1530  if (afw_object_has_property(test_script_object, key, parser->xctx))
1531  {
1532  AFW_COMPILE_THROW_ERROR_FZ(
1533  "%" AFW_UTF8_FMT ": already specified",
1534  AFW_UTF8_FMT_ARG(key));
1535  }
1536  if (afw_utf8_equal(key, &afw_s_sourceType)) {
1537  global_source_type = string;
1538  }
1539  afw_object_set_property_as_string(test_script_object,
1540  key, string, parser->xctx);
1541  }
1542  }
1543  if (!global_source_type) {
1544  global_source_type = &afw_s_script;
1545  afw_object_set_property_as_string(test_script_object,
1546  &afw_s_sourceType, global_source_type, parser->xctx);
1547  }
1548 
1549  /* Process TestDefinition */
1550  for (test_object = NULL; ;) {
1551 
1552  if (!key || afw_utf8_equal(key, &afw_s_test)) {
1553  if (test_object) {
1554  if (!afw_object_has_property(test_object,
1555  &afw_s_source, parser->xctx))
1556  {
1557  AFW_COMPILE_THROW_ERROR_Z("source: missing");
1558  }
1559  if (!afw_object_has_property(test_object,
1560  &afw_s_expect, parser->xctx))
1561  {
1562  AFW_COMPILE_THROW_ERROR_Z("expect: missing");
1563  }
1564  }
1565  if (!key) {
1566  break;
1567  }
1568  test_object = afw_object_create(parser->p, parser->xctx);
1569  afw_list_add_value(test_list,
1570  afw_value_create_object(test_object, parser->p, parser->xctx),
1571  parser->xctx);
1573  &afw_s_test, string, parser->xctx);
1574  }
1575 
1576  else if (afw_utf8_equal(key, &afw_s_skip)) {
1577  if (afw_utf8_equal(string, &afw_s_true)) {
1578  afw_object_set_property(test_object,
1579  key, afw_value_true, parser->xctx);
1580  }
1581  else if (!afw_utf8_equal(string, &afw_s_false)) {
1583  "skip: must be true or false");
1584  }
1585  }
1586 
1587  else {
1588  if (afw_object_has_property(test_object, key, parser->xctx)) {
1589  AFW_COMPILE_THROW_ERROR_FZ(
1590  "%" AFW_UTF8_FMT ": already specified",
1591  AFW_UTF8_FMT_ARG(key));
1592  }
1594  key, string, parser->xctx);
1595  }
1596 
1597  if (afw_utf8_equal(key, &afw_s_expect)) {
1599  &source_line, &source_column,
1600  parser->full_source,
1601  string_offset, 4, parser->xctx);
1602  expect_location = afw_utf8_printf(parser->p, parser->xctx,
1603  "%" AFW_UTF8_FMT
1604  "+%" AFW_SIZE_T_FMT
1605  "[%" AFW_SIZE_T_FMT ":%" AFW_SIZE_T_FMT "]",
1606  AFW_UTF8_FMT_ARG(test_script_id),
1607  string_offset, source_line, source_column);
1609  &afw_s_expectLocation, expect_location, parser->xctx);
1611  &afw_s_expectLineNumberInTestScript,
1612  afw_safe_cast_size_to_integer(source_line, parser->xctx),
1613  parser->xctx);
1615  &afw_s_expectColumnNumberInTestScript,
1616  afw_safe_cast_size_to_integer(source_column, parser->xctx),
1617  parser->xctx);
1619  &afw_s_expectCodepointOffsetInTestScript,
1620  afw_safe_cast_size_to_integer(string_offset, parser->xctx),
1621  parser->xctx);
1623  &afw_s_expectCodepointLengthInTestScript,
1624  afw_safe_cast_size_to_integer(string_length, parser->xctx),
1625  parser->xctx);
1627  &afw_s_expectUTF8OctetOffsetInTestScript,
1628  afw_safe_cast_size_to_integer(string_offset, parser->xctx),
1629  parser->xctx);
1631  &afw_s_expectUTF8OctetLengthInTestScript,
1632  afw_safe_cast_size_to_integer(string_length, parser->xctx),
1633  parser->xctx);
1634  }
1635 
1636  if (afw_utf8_equal(key, &afw_s_source)) {
1638  &source_line, &source_column,
1639  parser->full_source,
1640  string_offset, 4, parser->xctx);
1641  source_location = afw_utf8_printf(parser->p, parser->xctx,
1642  "%" AFW_UTF8_FMT
1643  "+%" AFW_SIZE_T_FMT
1644  "[%" AFW_SIZE_T_FMT ":%" AFW_SIZE_T_FMT "]",
1645  AFW_UTF8_FMT_ARG(test_script_id),
1646  string_offset, source_line, source_column);
1648  &afw_s_sourceLocation, source_location, parser->xctx);
1650  &afw_s_sourceLineNumberInTestScript,
1651  afw_safe_cast_size_to_integer(source_line, parser->xctx),
1652  parser->xctx);
1654  &afw_s_sourceColumnNumberInTestScript,
1655  afw_safe_cast_size_to_integer(source_column, parser->xctx),
1656  parser->xctx);
1658  &afw_s_sourceCodepointOffsetInTestScript,
1659  afw_safe_cast_size_to_integer(string_offset, parser->xctx),
1660  parser->xctx);
1662  &afw_s_sourceCodepointLengthInTestScript,
1663  afw_safe_cast_size_to_integer(string_length, parser->xctx),
1664  parser->xctx);
1666  &afw_s_sourceUTF8OctetOffsetInTestScript,
1667  afw_safe_cast_size_to_integer(string_offset, parser->xctx),
1668  parser->xctx);
1670  &afw_s_sourceUTF8OctetLengthInTestScript,
1671  afw_safe_cast_size_to_integer(string_length, parser->xctx),
1672  parser->xctx);
1673  }
1674 
1675  impl_test_script_get_next_key_value(parser,
1676  &key, &string, &string_offset, &string_length);
1677  }
1678 
1679  string = afw_utf8_create_copy(
1680  parser->full_source->s, parser->full_source->len,
1681  parser->p, parser->xctx);
1682  afw_object_set_property_as_string(test_script_object,
1683  &afw_s_source, string, parser->xctx);
1684 
1685  /* Result is call to test_script_runtime_support() with testScriptObject. */
1686  argv = afw_pool_malloc(parser->p,
1687  sizeof(afw_value_t *) * 2, parser->xctx);
1688  argv[0] = (const afw_value_t *)
1690  argv[1] = afw_value_create_object(test_script_object,
1691  parser->p, parser->xctx);
1693  afw_compile_create_contextual_to_cursor(start_offset),
1694  1, argv, parser->p, parser->xctx);
1695  return result;
1696 }
#define AFW_DEFINE_INTERNAL(type)
Define an internal function for /src/afw/ source*.c files.
Adaptive Framework Core Internal.
afw_object_set_property_as_integer(const afw_object_t *object, const afw_utf8_t *property_name, afw_integer_t internal, afw_xctx_t *xctx)
Set property function for data type integer values.
afw_value_create_list(const afw_list_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type list value.
afw_object_set_property_as_list(const afw_object_t *object, const afw_utf8_t *property_name, const afw_list_t *internal, afw_xctx_t *xctx)
Set property function for data type list values.
afw_data_type_object
Data type struct for object.
afw_value_create_object(const afw_object_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type object value.
afw_value_create_string(const afw_utf8_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type string value.
afw_object_set_property_as_string(const afw_object_t *object, const afw_utf8_t *property_name, const afw_utf8_t *internal, afw_xctx_t *xctx)
Set property function for data type string values.
#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
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
struct afw_compile_internal_args_s afw_compile_args_t
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_function_definition_test_script_runtime_support
Function definition test_script_runtime_support.
afw_function_definition_and
Function definition and.
afw_function_definition_or
Function definition or.
afw_function_definition_nullish_coalescing
Function definition nullish_coalescing.
afw_function_definition_pow
Adaptive Function one_and_only
afw_function_definition_multiply
Adaptive Function mod
afw_function_definition_divide
Adaptive Function decode_to_string
afw_function_definition_mod
Adaptive Function min
afw_function_definition_add
Adaptive Function abs
afw_function_definition_subtract
Adaptive Function substring
afw_function_definition_foreach
Function definition foreach.
afw_function_definition_if
Adaptive Function gt<script>
afw_function_definition_while
Adaptive Function script
afw_function_definition_loc
Adaptive Function le<script>
afw_function_definition_const
Function definition const.
afw_function_definition_continue
Function definition continue.
afw_function_definition_for
Function definition for.
afw_function_definition_assign
Function definition assign.
afw_function_definition_do_while
Function definition do_while.
afw_function_definition_break
Adaptive Function bag_size<script>
afw_function_definition_return
Adaptive Function nex<script>
#define afw_list_create_generic(p, xctx)
Create an value list in memory.
Definition: afw_list.h:81
afw_list_add_value(const afw_list_t *instance, const afw_value_t *value, afw_xctx_t *xctx)
Call method add_value of interface afw_list_setter.
Definition: afw_list.c:104
#define afw_list_of_create(data_type, p, xctx)
Create an list of a specific data type in memory.
Definition: afw_list.h:64
#define afw_object_has_property(instance, property_name, xctx)
Call method has_property of interface afw_object.
#define afw_object_create(p, xctx)
Create an empty unmanaged object in memory.
Definition: afw_object.h:948
afw_object_set_property(const afw_object_t *instance, const afw_utf8_t *property_name, const afw_value_t *value, afw_xctx_t *xctx)
Set the value of an object's property.
Definition: afw_object.c:46
#define afw_pool_malloc(instance, size, xctx)
Call method malloc of interface afw_pool.
afw_integer_t afw_safe_cast_size_to_integer(afw_size_t size, afw_xctx_t *xctx)
Safely cast afw_size_t to afw_integer_t.
#define afw_utf8_create_copy(s, len, p, xctx)
Make a utf-8 sting from chars in pool specified.
Definition: afw_utf8.h:369
afw_boolean_t afw_utf8_starts_with_z(const afw_utf8_t *string, const afw_utf8_z_t *starts_with_z)
Check to see if a string starts with a utf8_z string.
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_boolean_t afw_utf8_starts_with(const afw_utf8_t *string, const afw_utf8_t *starts_with)
Check to see if a string starts with another string.
const afw_value_t * afw_value_block_finalize(const afw_value_block_t *block, afw_size_t argc, const afw_value_t *const *argv, afw_xctx_t *xctx)
Create and link a new block.
const afw_value_t * afw_value_call_create(const afw_compile_value_contextual_t *contextual, afw_size_t argc, const afw_value_t *const *argv, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for call value.
const afw_value_t * afw_value_call_built_in_function_create(const afw_compile_value_contextual_t *contextual, afw_size_t argc, const afw_value_t *const *argv, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for call_built_in_function value.
afw_value_null
Adaptive value null.
Definition: afw_value.h:320
#define afw_value_is_any_call(A_VALUE)
Macro to determine if value is a call.
Definition: afw_value.h:572
afw_value_true
Adaptive value true.
Definition: afw_value.h:348
Interface afw_list public struct.
Interface afw_object public struct.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
struct for afw_value_block_t
Interface afw_value public struct.