summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChloe M. <chloe@aptel.org>2026-08-23 04:49:51 +0000
committerChloe M. <chloe@aptel.org>2026-08-23 04:49:51 +0000
commit78b925a4d86b57e58e6322b7cad7bc47098618fd (patch)
treee66da7a598bb4be1ca869f72600112dda34a4067
initial commit
There is some work to be done on top of this such as creating a way to manage memory to be freed later without needing to manually manage every pointer. Signed-off-by: Chloe M. <chloe@aptel.org>
-rw-r--r--.gitignore3
-rwxr-xr-xbuild.sh8
-rw-r--r--conf/defconf-00.conf2
-rw-r--r--core/.bop/cflags1
-rw-r--r--core/.bop/posthook.sh1
-rw-r--r--core/defconf.c70
-rw-r--r--core/lexer.c191
-rw-r--r--core/state.c35
-rw-r--r--head/defconf/lexer.h23
-rw-r--r--head/defconf/state.h41
-rw-r--r--head/defconf/token.h36
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 */