diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rwxr-xr-x | build.sh | 8 | ||||
| -rw-r--r-- | conf/defconf-00.conf | 2 | ||||
| -rw-r--r-- | core/.bop/cflags | 1 | ||||
| -rw-r--r-- | core/.bop/posthook.sh | 1 | ||||
| -rw-r--r-- | core/defconf.c | 70 | ||||
| -rw-r--r-- | core/lexer.c | 191 | ||||
| -rw-r--r-- | core/state.c | 35 | ||||
| -rw-r--r-- | head/defconf/lexer.h | 23 | ||||
| -rw-r--r-- | head/defconf/state.h | 41 | ||||
| -rw-r--r-- | head/defconf/token.h | 36 |
11 files changed, 411 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..114ee9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +*.hash +/defconf diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..ae01711 --- /dev/null +++ b/build.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# +# Copyright (c) 2026, Apollo Telephone Laboratories. +# Provided under the BSD-3 clause. +# + +BUILD_DIRS=core +BOP_BUILD=$BUILD_DIRS bop build diff --git a/conf/defconf-00.conf b/conf/defconf-00.conf new file mode 100644 index 0000000..e87e63a --- /dev/null +++ b/conf/defconf-00.conf @@ -0,0 +1,2 @@ +define NR_MEOW 4 +define MEOW_YES true diff --git a/core/.bop/cflags b/core/.bop/cflags new file mode 100644 index 0000000..1865e42 --- /dev/null +++ b/core/.bop/cflags @@ -0,0 +1 @@ +-Ihead diff --git a/core/.bop/posthook.sh b/core/.bop/posthook.sh new file mode 100644 index 0000000..e03550a --- /dev/null +++ b/core/.bop/posthook.sh @@ -0,0 +1 @@ +clang core/*.o -o defconf diff --git a/core/defconf.c b/core/defconf.c new file mode 100644 index 0000000..4444179 --- /dev/null +++ b/core/defconf.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2026, Apollo Telephone Laboratories. + * Provided under the BSD-3 clause. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "defconf/state.h" + +/* Globals */ +static char *config_path = NULL; + +static void +help(void) +{ + printf("usage: ./defconf [flags]\n"); + printf("[-h] Display this help menu\n"); + printf("[-c] Target config file\n"); +} + +static int +defconf_run(void) +{ + struct defconf_state state; + + if (defconf_state_init(config_path, &state) < 0) { + return -1; + } + + defconf_state_destroy(&state); + return 0; +} + +int +main(int argc, char **argv) +{ + int opt, retval; + + if (argc < 2) { + printf("fatal: too few arguments\n"); + help(); + return -1; + } + + while ((opt = getopt(argc, argv, "hc:")) != -1) { + switch (opt) { + case 'h': + help(); + return -1; + case 'c': + if ((config_path = strdup(optarg)) == NULL) { + printf("fatal: out of memory\n"); + return -1; + } + + break; + } + } + + if (config_path == NULL) { + printf("fatal: expected config path\n"); + return -1; + } + + retval = defconf_run(); + free(config_path); + return retval; +} diff --git a/core/lexer.c b/core/lexer.c new file mode 100644 index 0000000..2eb50a1 --- /dev/null +++ b/core/lexer.c @@ -0,0 +1,191 @@ +#include <ctype.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include "defconf/lexer.h" + +/* Constants */ +#define MAX_KW_LEN 32 + +/* + * Returns true if the given character is a whitespace character + * + * @c: Character to check + */ +static inline bool +lexer_is_ws(char c) +{ + switch (c) { + case '\t': + case ' ': + case '\f': + case '\r': + case '\n': + return true; + } + + return false; +} + +/* + * Place a character back in the putback buffer + * + * @state: Defconf state + * @c: Character to put back + */ +static inline void +lexer_putback(struct defconf_state *state, int c) +{ + if (state == NULL) { + return; + } + + state->lex_cache = c; +} + +/* + * Nom a single character from the input source file + * + * @state: Defconf state machine + * @skip_ws: If true, skip whitespace + * + * Returns the character on success, otherwise EOF on failure + */ +static int +lexer_nom(struct defconf_state *state, bool skip_ws) +{ + int c; + + if (state == NULL) { + return EOF; + } + + if ((c = state->lex_cache) != '\0') { + state->lex_cache = '\0'; + if (lexer_is_ws(c) && !skip_ws) + return c; + if (!lexer_is_ws(c)) + return c; + } + + while ((c = fgetc(state->in_fp)) != EOF) { + if (lexer_is_ws(c) && skip_ws) + continue; + + break; + } + + return c; +} + +/* + * Scan a keyword + * + * @state: Defconf state + * @lc: Last character provided + */ +static char * +lexer_scan_ident(struct defconf_state *state, int lc) +{ + char c, buf[MAX_KW_LEN]; + size_t bufind = 0; + + if (state == NULL) { + return NULL; + } + + if (!isalpha(lc)) { + return NULL; + } + + buf[bufind++] = lc; + for (;;) { + if (bufind >= sizeof(buf) - 1) { + printf("fatal: token exceeds maximum length\n"); + return NULL; + } + + c = lexer_nom(state, false); + if (!isalnum(c) && c != '_') { + lexer_putback(state, c); + buf[bufind] = '\0'; + return strdup(buf); + } + + buf[bufind++] = c; + } + + return NULL; +} + +/* + * Check if a keyword matches against known keywords + * + * @kw: Keyword to check + * @result: Result is written here + * + * Returns zero on success + */ +static int +lexer_check_kw(const char *kw, struct token *result) +{ + if (kw == NULL || result == NULL) { + return -1; + } + + switch (*kw) { + case 'd': + if (strcmp(kw, "define") == 0) { + result->type = TT_DEFINE; + return 0; + } + + break; + case 't': + if (strcmp(kw, "true") == 0) { + result->type = TT_TRUE; + return 0; + } + + break; + case 'f': + if (strcmp(kw, "false") == 0) { + result->type = TT_FALSE; + return 0; + } + + break; + } + + return -1; +} + +int +lexer_scan(struct defconf_state *state, struct token *result) +{ + int c; + char *ident; + + if (state == NULL || result == NULL) { + return -1; + } + + if ((c = lexer_nom(state, true)) == EOF) { + return -1; + } + + /* Scan for an identifier */ + if ((ident = lexer_scan_ident(state, c)) != NULL) { + if (lexer_check_kw(ident, result) == 0) { + free(ident); + return 0; + } + + result->type = TT_IDENT; + result->s = ident; + return 0; + } + + printf("fatal: unexpected token '%c'\n", c); + return -1; +} diff --git a/core/state.c b/core/state.c new file mode 100644 index 0000000..ce1c286 --- /dev/null +++ b/core/state.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, Apollo Telephone Laboratories. + * Provided under the BSD-3 clause. + */ + +#include <stdio.h> +#include "defconf/state.h" + +int +defconf_state_init(const char *in_path, struct defconf_state *state) +{ + if (in_path == NULL || state == NULL) { + return -1; + } + + state->in_fp = fopen(in_path, "r"); + if (state->in_fp == NULL) { + perror("fopen"); + return -1; + } + + state->lex_cache = '\0'; + return 0; +} + +void +defconf_state_destroy(struct defconf_state *state) +{ + if (state == NULL) { + return; + } + + fclose(state->in_fp); + state->in_fp = NULL; +} diff --git a/head/defconf/lexer.h b/head/defconf/lexer.h new file mode 100644 index 0000000..6fb3828 --- /dev/null +++ b/head/defconf/lexer.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026, Apollo Telephone Laboratories. + * Provided under the BSD-3 clause. + */ + +#ifndef DEFCONF_LEXER_H +#define DEFCONF_LEXER_H 1 + +#include <stdint.h> +#include "defconf/token.h" +#include "defconf/state.h" + +/* + * Scan for a single token + * + * @state: Defconf state machine + * @result: Result is written here + * + * Returns zero + */ +int lexer_scan(struct defconf_state *state, struct token *result); + +#endif /* !_DEFCONF_LEXER_H_ */ diff --git a/head/defconf/state.h b/head/defconf/state.h new file mode 100644 index 0000000..524af3b --- /dev/null +++ b/head/defconf/state.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Apollo Telephone Laboratories. + * Provided under the BSD-3 clause. + */ + +#ifndef DEFCONF_STATE_H +#define DEFCONF_STATE_H 1 + +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> + +/* + * DEFCONF state machine + * + * @in_fp: Input file pointer + * @lex_cache: Putback cache + */ +struct defconf_state { + FILE *in_fp; + int lex_cache; +}; + +/* + * Initialize the defconf state machine + * + * @in_path: Input path + * @state: State machine result + * + * Returns zero on success + */ +int defconf_state_init(const char *in_path, struct defconf_state *state); + +/* + * Destroy the defconf state machine + * + * @state: State machine to destroy + */ +void defconf_state_destroy(struct defconf_state *state); + +#endif /* !DEFCONF_STATE_H */ diff --git a/head/defconf/token.h b/head/defconf/token.h new file mode 100644 index 0000000..e1abd0a --- /dev/null +++ b/head/defconf/token.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026, Apollo Telephone Laboratories. + * Provided under the BSD-3 clause. + */ + +#ifndef DEFCONF_TOKEN_H +#define DEFCONF_TOKEN_H 1 + +/* + * Valid token types + */ +typedef enum { + TT_NONE, /* [none] */ + TT_DEFINE, /* 'define' */ + TT_TRUE, /* 'true' */ + TT_FALSE, /* 'false' */ + TT_INTLIT, /* [0-9]+ */ + TT_IDENT, /* [ident] */ +} tt_t; + +/* + * Represents a token + * + * @type: Token type + * @c: Associated character + * @s: Associated string + */ +struct token { + tt_t type; + union { + char c; + char *s; + }; +}; + +#endif /* !DEFCONF_TOKEN_H */ |
