/* * * Copyright (c) 2016, Masatake YAMATO * Copyright (c) 2016, Red Hat, K.K. * * This source code is released for free distribution under the terms of the * GNU General Public License version 2 or (at your option) any later version. * */ #include "general.h" /* must always come first */ #include "debug.h" #include "entry.h" #include "kind.h" #include "yaml.h" #include "parse.h" #include "subparser.h" #include typedef enum { K_PLAY } ansiblePlaybookKind; static kindDefinition AnsiblePlaybookKinds [] = { { true, 'p', "play", "plays" }, }; struct yamlBlockTypeStack { yaml_token_type_t type; int associatedCorkIndex; struct yamlBlockTypeStack *next; }; /* - name: "THE NAME" */ enum ansiblePlaybookPlayDetectingState { DSTAT_PLAY_NAME_INITIAL, DSTAT_PLAY_NAME_KEY, DSTAT_PLAY_NAME_KEY_SCALAR, DSTAT_PLAY_NAME_VALUE, }; struct sAnsiblePlaybookSubparser { yamlSubparser yaml; struct yamlBlockTypeStack *type_stack; enum ansiblePlaybookPlayDetectingState play_detection_state; }; static void pushBlockType (struct sAnsiblePlaybookSubparser *ansible, yaml_token_type_t t) { struct yamlBlockTypeStack *s; s = xMalloc (1, struct yamlBlockTypeStack); s->next = ansible->type_stack; ansible->type_stack = s; s->type = t; s->associatedCorkIndex = CORK_NIL; } static void popBlockType (struct sAnsiblePlaybookSubparser *ansible, yaml_token_t *token) { struct yamlBlockTypeStack *s; s = ansible->type_stack; ansible->type_stack = s->next; s->next = NULL; tagEntryInfo *tag = getEntryInCorkQueue (s->associatedCorkIndex); if (tag && token) attachYamlPosition (tag, token, true); eFree (s); } static void popAllBlockType (struct sAnsiblePlaybookSubparser *ansible, yaml_token_t *token) { while (ansible->type_stack) popBlockType (ansible, token); } static bool stateStackMatch (struct yamlBlockTypeStack *stack, yaml_token_type_t *expectation, unsigned int length, bool partial) { if (length == 0) { if (stack == NULL) return true; else if (partial) return true; else return false; } if (stack == NULL) return false; if (stack->type == expectation[0]) return stateStackMatch (stack->next, expectation + 1, length - 1, partial); else return false; } static bool scalarNeq (yaml_token_t *token, unsigned int len, const char* val) { if ((token->data.scalar.length == len) && (strncmp (val, (char *)token->data.scalar.value, len) == 0)) return true; else return false; } static void ansiblePlaybookPlayStateMachine (struct sAnsiblePlaybookSubparser *ansible, yaml_token_t *token) { yaml_token_type_t play_expect[] = { YAML_BLOCK_MAPPING_START_TOKEN, YAML_BLOCK_SEQUENCE_START_TOKEN, }; switch (token->type) { case YAML_KEY_TOKEN: if (stateStackMatch (ansible->type_stack, play_expect, ARRAY_SIZE (play_expect), false)) ansible->play_detection_state = DSTAT_PLAY_NAME_KEY; else ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL; break; case YAML_SCALAR_TOKEN: if ((ansible->play_detection_state == DSTAT_PLAY_NAME_KEY) && scalarNeq (token, 4, "name")) ansible->play_detection_state = DSTAT_PLAY_NAME_KEY_SCALAR; else if (ansible->play_detection_state == DSTAT_PLAY_NAME_VALUE) { tagEntryInfo tag; initTagEntry (&tag, (char *)token->data.scalar.value, K_PLAY); attachYamlPosition (&tag, token, false); Assert (ansible->type_stack->associatedCorkIndex == CORK_NIL); ansible->type_stack->associatedCorkIndex = makeTagEntry (&tag); ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL; } else ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL; break; case YAML_VALUE_TOKEN: if (ansible->play_detection_state == DSTAT_PLAY_NAME_KEY_SCALAR) ansible->play_detection_state = DSTAT_PLAY_NAME_VALUE; else ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL; break; default: ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL; break; } } static void newTokenCallback (yamlSubparser *s, yaml_token_t *token) { if (token->type == YAML_BLOCK_SEQUENCE_START_TOKEN || token->type == YAML_BLOCK_MAPPING_START_TOKEN) pushBlockType ((struct sAnsiblePlaybookSubparser *)s, token->type); ansiblePlaybookPlayStateMachine ((struct sAnsiblePlaybookSubparser *)s, token); if (token->type == YAML_BLOCK_END_TOKEN) popBlockType ((struct sAnsiblePlaybookSubparser *)s, token); else if (token->type == YAML_STREAM_END_TOKEN) popAllBlockType ((struct sAnsiblePlaybookSubparser *)s, token); } static void inputStart(subparser *s) { ((struct sAnsiblePlaybookSubparser*)s)->play_detection_state = DSTAT_PLAY_NAME_INITIAL; ((struct sAnsiblePlaybookSubparser*)s)->type_stack = NULL; } static void inputEnd(subparser *s) { popAllBlockType ((struct sAnsiblePlaybookSubparser *)s, NULL); Assert (((struct sAnsiblePlaybookSubparser*)s)->type_stack == NULL); } static void findAnsiblePlaybookTags (void) { scheduleRunningBaseparser (0); } extern parserDefinition* AnsiblePlaybookParser (void) { static struct sAnsiblePlaybookSubparser ansiblePlaybookSubparser = { .yaml = { .subparser = { .direction = SUBPARSER_BI_DIRECTION, .inputStart = inputStart, .inputEnd = inputEnd, }, .newTokenNotfify = newTokenCallback }, }; static parserDependency dependencies [] = { { DEPTYPE_SUBPARSER, "Yaml", &ansiblePlaybookSubparser }, }; parserDefinition* const def = parserNew ("AnsiblePlaybook"); def->dependencies = dependencies; def->dependencyCount = ARRAY_SIZE (dependencies); def->kindTable = AnsiblePlaybookKinds; def->kindCount = ARRAY_SIZE (AnsiblePlaybookKinds); def->parser = findAnsiblePlaybookTags; def->useCork = CORK_QUEUE; return def; } /* vi:set tabstop=4 shiftwidth=4: */