summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Mikkelsen <petermikkelsen10@gmail.com>2026-04-19 17:09:06 +0200
committerPeter Mikkelsen <petermikkelsen10@gmail.com>2026-04-19 17:09:06 +0200
commitc9f1161ecb323c7872559dd40c56d691dbd5959f (patch)
treebc6b974c56d55e11a78cc10bd7da399e54d307cd
parent984ca5a2330ce29b62892321f258d5a0afb0091c (diff)
Start working on parsing/scanning.
Too many changes to list them all individually.
-rw-r--r--.gitignore3
-rw-r--r--lib/Makefile.am16
-rw-r--r--lib/aplwc.h11
-rw-r--r--lib/aplwc_internal.h63
-rw-r--r--lib/autocomplete.c24
-rw-r--r--lib/eval.c38
-rw-r--r--lib/exit.c28
-rw-r--r--lib/free_ast.c38
-rw-r--r--lib/free_eval_context.c70
-rw-r--r--lib/new.c11
-rw-r--r--lib/new_eval_context.c35
-rw-r--r--lib/output.c31
-rw-r--r--lib/parse.c100
-rw-r--r--lib/run_line.c34
-rw-r--r--lib/running.c28
-rw-r--r--lib/scan_line.c161
-rw-r--r--lib/strdup.c32
-rw-r--r--lib/syscmd_error.c32
-rw-r--r--lib/syscmd_output.c32
-rw-r--r--lib/voutput.c30
-rw-r--r--po/da.po46
-rw-r--r--src/main.c9
-rw-r--r--src/readline.c2
-rw-r--r--src/syscmd.c51
24 files changed, 891 insertions, 34 deletions
diff --git a/.gitignore b/.gitignore
index eba84db..e5afc7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,6 @@ aplwc-*.tar.*
/scripts/publish-release-docs
/doc/aplwc.cp
/doc/aplwc.cps
+/.gdb_history
+/lib/.gdb_history
+/src/.gdb_history
diff --git a/lib/Makefile.am b/lib/Makefile.am
index ac8555b..9644940 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,9 +4,23 @@ noinst_HEADERS = aplwc_internal.h
libaplwc_la_SOURCES = \
autocomplete.c \
+ eval.c \
+ exit.c \
+ free_ast.c \
+ free_eval_context.c \
lookup_syscmd.c \
new.c \
- register_syscmd.c
+ new_eval_context.c \
+ output.c \
+ parse.c \
+ register_syscmd.c \
+ run_line.c \
+ running.c \
+ scan_line.c \
+ strdup.c \
+ syscmd_output.c \
+ syscmd_error.c \
+ voutput.c
libaplwc_la_LDFLAGS = -version-info 0:0:0
diff --git a/lib/aplwc.h b/lib/aplwc.h
index aef2495..6bfdf40 100644
--- a/lib/aplwc.h
+++ b/lib/aplwc.h
@@ -21,14 +21,23 @@
#ifndef APLWC_H
#define APLWC_H
+#include <stddef.h>
+#include <stdbool.h>
+
struct aplwc;
struct aplwc_syscmd {
const char *name;
+ void (*run)(struct aplwc *, struct aplwc_syscmd *, char *);
};
-struct aplwc *aplwc_new(void);
+struct aplwc *aplwc_new(void *(*)(size_t), void (*)(void *), void *(*)(void *, size_t));
char **aplwc_autocomplete(struct aplwc *, const char *, int, int);
void aplwc_register_syscmd(struct aplwc *, struct aplwc_syscmd *);
+void aplwc_syscmd_error(struct aplwc *, struct aplwc_syscmd *, const char *, ...);
+void aplwc_syscmd_output(struct aplwc *, struct aplwc_syscmd *, const char *, ...);
+void aplwc_run_line(struct aplwc *, const char *);
+void aplwc_exit(struct aplwc *);
+bool aplwc_running(struct aplwc *);
#endif /* APLWC_H */
diff --git a/lib/aplwc_internal.h b/lib/aplwc_internal.h
index 99c16ee..dd676fb 100644
--- a/lib/aplwc_internal.h
+++ b/lib/aplwc_internal.h
@@ -21,11 +21,74 @@
#ifndef APLWC_INTERNAL_H
#define APLWC_INTERNAL_H
+#include <stdarg.h>
+#include <stdbool.h>
+
+enum aplwc_token_tag {
+ APLWC_TOKEN_SYSCMD,
+ APLWC_TOKEN_SYSCMD_ARGS,
+
+ APLWC_TOKEN_ERROR,
+};
+
+enum aplwc_ast_tag {
+ APLWC_AST_SYSCMD,
+
+ APLWC_AST_ERROR,
+};
+
struct aplwc {
+ void *(*alloc)(size_t);
+ void (*free)(void *);
+ void *(*realloc)(void *, size_t);
+
struct aplwc_syscmd **syscmds;
int n_syscmds;
+ bool running;
+};
+
+struct aplwc_eval_context {
+ struct aplwc *aplwc;
+ char *text;
+ size_t offset;
+ size_t length;
+
+ size_t n_tokens;
+ struct aplwc_token **tokens;
+
+ size_t token_offset;
+ struct aplwc_ast *ast;
+};
+
+struct aplwc_token {
+ enum aplwc_token_tag tag;
+ size_t offset_start;
+ size_t offset_end;
+
+ union {
+ } data;
+};
+
+struct aplwc_ast {
+ enum aplwc_ast_tag tag;
+
+ union {
+ struct {
+ struct aplwc_syscmd *syscmd;
+ char *args;
+ } syscmd;
+ } data;
};
struct aplwc_syscmd *aplwc_lookup_syscmd(struct aplwc *, const char *);
+struct aplwc_eval_context *aplwc_new_eval_context(struct aplwc *);
+void aplwc_scan_line(struct aplwc_eval_context *, const char *);
+void aplwc_parse(struct aplwc_eval_context *);
+void aplwc_eval(struct aplwc_eval_context *);
+void aplwc_free_eval_context(struct aplwc_eval_context *);
+void aplwc_free_ast(struct aplwc *, struct aplwc_ast *);
+char *aplwc_strdup(struct aplwc *, const char *);
+void aplwc_output(struct aplwc *, const char *, ...);
+void aplwc_voutput(struct aplwc *, const char *, va_list);
#endif /* APLWC_INTERNAL_H */
diff --git a/lib/autocomplete.c b/lib/autocomplete.c
index 12a8cdc..9a33ea3 100644
--- a/lib/autocomplete.c
+++ b/lib/autocomplete.c
@@ -44,10 +44,24 @@ aplwc_autocomplete(struct aplwc *aplwc, const char *line, int start, int end)
memcpy(word, line+start, len);
word[len] = 0;
- if(start == 0 && word[0] == ')')
- completions = generate(aplwc, word, completions_syscmd);
+ struct aplwc_eval_context *context = aplwc_new_eval_context(aplwc);
+ aplwc_scan_line(context, line);
+ struct aplwc_token *tok = NULL;
+ for(size_t i = 0; (i < context->n_tokens) && (tok == NULL); i++)
+ if(((size_t)start >= context->tokens[i]->offset_start) && ((size_t)end <= context->tokens[i]->offset_end + 1))
+ tok = context->tokens[i];
+
+ if(tok)
+ switch(tok->tag) {
+ case APLWC_TOKEN_SYSCMD:
+ completions = generate(aplwc, word, completions_syscmd);
+ break;
+ default:
+ break;
+ }
free(word);
-
+
+ aplwc_free_eval_context(context);
return completions;
}
@@ -64,7 +78,7 @@ generate(struct aplwc *aplwc, const char *word, char *(*fn)(struct completion_st
char *c;
int index = 0;
- while(c = fn(&state)){
+ while((c = fn(&state))){
index++;
completions = realloc(completions, sizeof(*completions) * (index + 1));
completions[index-1] = c;
@@ -79,7 +93,7 @@ completions_syscmd(struct completion_state *state)
{
struct aplwc_syscmd *syscmd;
- while(syscmd = state->aplwc->syscmds[state->num++]){
+ while((syscmd = state->aplwc->syscmds[state->num++])){
const char *name = syscmd->name;
if(strncmp(name, state->word+1, state->wordlen-1) == 0){
int len = strlen(name);
diff --git a/lib/eval.c b/lib/eval.c
new file mode 100644
index 0000000..6eaaddf
--- /dev/null
+++ b/lib/eval.c
@@ -0,0 +1,38 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+void
+aplwc_eval(struct aplwc_eval_context *context)
+{
+ struct aplwc_syscmd *syscmd;
+
+ switch(context->ast->tag){
+ case APLWC_AST_SYSCMD:
+ syscmd = context->ast->data.syscmd.syscmd;
+ syscmd->run(context->aplwc, syscmd, context->ast->data.syscmd.args);
+ break;
+ case APLWC_AST_ERROR:
+ printf("cannot evaluate since parsing failed\n");
+ }
+}
diff --git a/lib/exit.c b/lib/exit.c
new file mode 100644
index 0000000..d2c89b8
--- /dev/null
+++ b/lib/exit.c
@@ -0,0 +1,28 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+void
+aplwc_exit(struct aplwc *aplwc)
+{
+ aplwc->running = false;
+}
diff --git a/lib/free_ast.c b/lib/free_ast.c
new file mode 100644
index 0000000..5db6633
--- /dev/null
+++ b/lib/free_ast.c
@@ -0,0 +1,38 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+void
+aplwc_free_ast(struct aplwc *aplwc, struct aplwc_ast *ast)
+{
+ if(ast == NULL)
+ return;
+
+ switch(ast->tag){
+ case APLWC_AST_SYSCMD:
+ break;
+ case APLWC_AST_ERROR:
+ break;
+ }
+
+ aplwc->free(ast);
+}
diff --git a/lib/free_eval_context.c b/lib/free_eval_context.c
new file mode 100644
index 0000000..5af5220
--- /dev/null
+++ b/lib/free_eval_context.c
@@ -0,0 +1,70 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+static void free_token(struct aplwc_eval_context *, struct aplwc_token *);
+static void free_ast(struct aplwc_eval_context *, struct aplwc_ast *);
+
+void
+aplwc_free_eval_context(struct aplwc_eval_context *context)
+{
+ context->aplwc->free(context->text);
+ for(size_t i = 0; i < context->n_tokens; i++)
+ free_token(context, context->tokens[i]);
+ free_ast(context, context->ast);
+ context->aplwc->free(context->tokens);
+ context->aplwc->free(context);
+}
+
+static void
+free_token(struct aplwc_eval_context *context, struct aplwc_token *tok)
+{
+ if(tok == NULL)
+ return;
+
+ switch(tok->tag){
+ case APLWC_TOKEN_SYSCMD:
+ break;
+ case APLWC_TOKEN_SYSCMD_ARGS:
+ break;
+ case APLWC_TOKEN_ERROR:
+ break;
+ }
+
+ context->aplwc->free(tok);
+}
+
+static void
+free_ast(struct aplwc_eval_context *context, struct aplwc_ast *ast)
+{
+ if(ast == NULL)
+ return;
+
+ switch(ast->tag){
+ case APLWC_AST_SYSCMD:
+ break;
+ case APLWC_AST_ERROR:
+ break;
+ }
+
+ context->aplwc->free(ast);
+}
diff --git a/lib/new.c b/lib/new.c
index 4454f47..6c11fca 100644
--- a/lib/new.c
+++ b/lib/new.c
@@ -24,13 +24,18 @@
#include "aplwc_internal.h"
struct aplwc *
-aplwc_new(void)
+aplwc_new(void *(*alloc)(size_t), void (*free)(void *), void *(*realloc)(void *, size_t))
{
- struct aplwc *aplwc = malloc(sizeof(*aplwc));
+ struct aplwc *aplwc = alloc(sizeof(*aplwc));
memset(aplwc, 0, sizeof(*aplwc));
- aplwc->syscmds = malloc(sizeof(aplwc->syscmds[0]));
+ aplwc->alloc = alloc;
+ aplwc->free = free;
+ aplwc->realloc = realloc;
+
+ aplwc->syscmds = aplwc->alloc(sizeof(aplwc->syscmds[0]));
aplwc->syscmds[0] = NULL;
+ aplwc->running = true;
return aplwc;
}
diff --git a/lib/new_eval_context.c b/lib/new_eval_context.c
new file mode 100644
index 0000000..01b5656
--- /dev/null
+++ b/lib/new_eval_context.c
@@ -0,0 +1,35 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+struct aplwc_eval_context *
+aplwc_new_eval_context(struct aplwc *aplwc)
+{
+ struct aplwc_eval_context *context = aplwc->alloc(sizeof(*context));
+ memset(context, 0, sizeof(*context));
+
+ context->aplwc = aplwc;
+
+ return context;
+}
diff --git a/lib/output.c b/lib/output.c
new file mode 100644
index 0000000..87fa766
--- /dev/null
+++ b/lib/output.c
@@ -0,0 +1,31 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+void
+aplwc_output(struct aplwc *aplwc, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ aplwc_voutput(aplwc, fmt, args);
+ va_end(args);
+}
diff --git a/lib/parse.c b/lib/parse.c
new file mode 100644
index 0000000..0bf2396
--- /dev/null
+++ b/lib/parse.c
@@ -0,0 +1,100 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+static enum aplwc_token_tag peek(struct aplwc_eval_context *);
+static struct aplwc_token *match(struct aplwc_eval_context *, enum aplwc_token_tag);
+static void emit(struct aplwc_eval_context *, enum aplwc_ast_tag, struct aplwc_ast **);
+static char *token_text(struct aplwc_eval_context *, struct aplwc_token *);
+static void parse_syscmd(struct aplwc_eval_context *, struct aplwc_ast **);
+
+void
+aplwc_parse(struct aplwc_eval_context *context)
+{
+ context->token_offset = 0;
+
+ if(peek(context) == APLWC_TOKEN_SYSCMD)
+ parse_syscmd(context, &context->ast);
+
+ if(context->ast == NULL)
+ emit(context, APLWC_AST_ERROR, &context->ast);
+}
+
+static enum aplwc_token_tag
+peek(struct aplwc_eval_context *context)
+{
+ enum aplwc_token_tag tag = -1;
+
+ if(context->token_offset < context->n_tokens)
+ tag = context->tokens[context->token_offset]->tag;
+ return tag;
+}
+
+static struct aplwc_token *
+match(struct aplwc_eval_context *context, enum aplwc_token_tag tag)
+{
+ struct aplwc_token *tok = NULL;
+
+ if(peek(context) == tag)
+ tok = context->tokens[context->token_offset++];
+ return tok;
+}
+
+static void
+emit(struct aplwc_eval_context *context, enum aplwc_ast_tag tag, struct aplwc_ast **ast)
+{
+ if((*ast) != NULL)
+ aplwc_free_ast(context->aplwc, *ast);
+
+ *ast = context->aplwc->alloc(sizeof(**ast));
+ memset(*ast, 0, sizeof(**ast));
+ (*ast)->tag = tag;
+}
+
+static char *
+token_text(struct aplwc_eval_context *context, struct aplwc_token *tok)
+{
+ size_t len = tok->offset_end - tok->offset_start + 1;
+ char *text = context->aplwc->alloc(len + 1);
+ memcpy(text, context->text + tok->offset_start, len);
+ text[len] = 0;
+ return text;
+}
+
+static void
+parse_syscmd(struct aplwc_eval_context *context, struct aplwc_ast **ast)
+{
+ struct aplwc_token *tok = match(context, APLWC_TOKEN_SYSCMD);
+
+ char *name = token_text(context, tok);
+ struct aplwc_syscmd *syscmd = aplwc_lookup_syscmd(context->aplwc, name+1);
+ context->aplwc->free(name);
+
+ if(syscmd){
+ emit(context, APLWC_AST_SYSCMD, ast);
+ (*ast)->data.syscmd.syscmd = syscmd;
+ (*ast)->data.syscmd.args = aplwc_strdup(context->aplwc, "");
+ }else
+ emit(context, APLWC_AST_ERROR, ast);
+}
diff --git a/lib/run_line.c b/lib/run_line.c
new file mode 100644
index 0000000..22f6055
--- /dev/null
+++ b/lib/run_line.c
@@ -0,0 +1,34 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+void
+aplwc_run_line(struct aplwc *aplwc, const char *line)
+{
+ struct aplwc_eval_context *context = aplwc_new_eval_context(aplwc);
+
+ aplwc_scan_line(context, line);
+ aplwc_parse(context);
+ aplwc_eval(context);
+ aplwc_free_eval_context(context);
+}
diff --git a/lib/running.c b/lib/running.c
new file mode 100644
index 0000000..3a235ff
--- /dev/null
+++ b/lib/running.c
@@ -0,0 +1,28 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+bool
+aplwc_running(struct aplwc *aplwc)
+{
+ return aplwc->running;
+}
diff --git a/lib/scan_line.c b/lib/scan_line.c
new file mode 100644
index 0000000..811c7cb
--- /dev/null
+++ b/lib/scan_line.c
@@ -0,0 +1,161 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+static void scan(struct aplwc_eval_context *);
+static struct aplwc_token *emit(struct aplwc_eval_context *, enum aplwc_token_tag, size_t);
+static bool at_end(struct aplwc_eval_context *);
+static void skip_whitespace(struct aplwc_eval_context *);
+static char peek(struct aplwc_eval_context *, size_t);
+static void consume(struct aplwc_eval_context *, size_t);
+static void give_up(struct aplwc_eval_context *);
+static bool scan_syscmd(struct aplwc_eval_context *);
+
+void
+aplwc_scan_line(struct aplwc_eval_context *context, const char *line)
+{
+ size_t linelen = strlen(line);
+
+ if(context->text){
+ size_t textlen = strlen(context->text);
+ context->text = context->aplwc->realloc(context->text, textlen + linelen + 2);
+ context->text[textlen] = '\n';
+ memcpy(context->text+textlen+1, line, linelen + 1);
+ }else{
+ context->text = context->aplwc->alloc(linelen+1);
+ memcpy(context->text, line, linelen + 1);
+ }
+
+ if(context->tokens){
+ for(size_t i = 0; i < context->n_tokens; i++)
+ context->aplwc->free(context->tokens[i]);
+ context->aplwc->free(context->tokens);
+ }
+ scan(context);
+}
+
+static void
+scan(struct aplwc_eval_context *context)
+{
+ context->n_tokens = 0;
+ context->tokens = NULL;
+ context->offset = 0;
+ context->length = strlen(context->text);
+
+ while(!at_end(context)){
+ bool scanned = false;
+
+ skip_whitespace(context);
+ if(at_end(context))
+ continue;
+
+ if(context->n_tokens == 0)
+ scanned |= scan_syscmd(context);
+
+ if(!scanned)
+ give_up(context);
+ }
+}
+
+static struct aplwc_token *
+emit(struct aplwc_eval_context *context, enum aplwc_token_tag tag, size_t length)
+{
+ struct aplwc_token *tok = context->aplwc->alloc(sizeof(*tok));
+ context->n_tokens++;
+ context->tokens = context->aplwc->realloc(context->tokens, sizeof(*context->tokens) * context->n_tokens);
+ context->tokens[context->n_tokens-1] = tok;
+
+ memset(tok, 0, sizeof(*tok));
+ tok->tag = tag;
+ tok->offset_start = context->offset;
+ tok->offset_end = tok->offset_start + length - 1;
+
+ consume(context, length);
+
+ return tok;
+}
+
+static bool
+at_end(struct aplwc_eval_context *context)
+{
+ return context->offset == context->length;
+}
+
+static void
+skip_whitespace(struct aplwc_eval_context *context)
+{
+ for(;;){
+ char c = peek(context, 0);
+ if((c == ' ') || (c == '\t'))
+ consume(context, 1);
+ else
+ break;
+ }
+}
+
+static char
+peek(struct aplwc_eval_context *context, size_t offset)
+{
+ size_t index = context->offset + offset;
+ if(index < context->length)
+ return context->text[index];
+ else
+ return 0;
+}
+
+static void
+consume(struct aplwc_eval_context *context, size_t n)
+{
+ context->offset += n;
+}
+
+static void
+give_up(struct aplwc_eval_context *context)
+{
+ emit(context, APLWC_TOKEN_ERROR, context->length - context->offset);
+}
+
+static bool
+scan_syscmd(struct aplwc_eval_context *context)
+{
+ if(peek(context, 0) != ')')
+ return false;
+
+ size_t len;
+
+ /* Scan the syscmd itself */
+ for(len = 1; !((peek(context, len) == 0) || (peek(context, len) == ' ')); len++);
+ emit(context, APLWC_TOKEN_SYSCMD, len);
+
+ /* Skip the leading whitespace */
+ skip_whitespace(context);
+
+ /* Take the rest of the line as arguments */
+ for(len = 0; !((peek(context, len) == 0) || (peek(context, len) == '\n')); len++)
+ printf("arglen = %zu\n", len);
+ if(len > 0)
+ emit(context, APLWC_TOKEN_SYSCMD_ARGS, len);
+
+ return true;
+}
diff --git a/lib/strdup.c b/lib/strdup.c
new file mode 100644
index 0000000..3629de6
--- /dev/null
+++ b/lib/strdup.c
@@ -0,0 +1,32 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+char *
+aplwc_strdup(struct aplwc *aplwc, const char *str)
+{
+ size_t len = strlen(str) + 1;
+ char *x = aplwc->alloc(len);
+ memcpy(x, str, len);
+ return x;
+}
diff --git a/lib/syscmd_error.c b/lib/syscmd_error.c
new file mode 100644
index 0000000..edee676
--- /dev/null
+++ b/lib/syscmd_error.c
@@ -0,0 +1,32 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+void
+aplwc_syscmd_error(struct aplwc *aplwc, struct aplwc_syscmd *syscmd, const char *fmt, ...)
+{
+ (void)aplwc;
+ (void)syscmd;
+ (void)fmt;
+ printf("bla");
+}
diff --git a/lib/syscmd_output.c b/lib/syscmd_output.c
new file mode 100644
index 0000000..3e8702d
--- /dev/null
+++ b/lib/syscmd_output.c
@@ -0,0 +1,32 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+void
+aplwc_syscmd_output(struct aplwc *aplwc, struct aplwc_syscmd *syscmd, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ aplwc_voutput(aplwc, fmt, args);
+ aplwc_output(aplwc, "\n");
+ va_end(args);
+}
diff --git a/lib/voutput.c b/lib/voutput.c
new file mode 100644
index 0000000..3f083ef
--- /dev/null
+++ b/lib/voutput.c
@@ -0,0 +1,30 @@
+/* Aplwc - A Programming Language With Constraints
+ *
+ * Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
+ *
+ * This file is part of aplwc.
+ *
+ * Aplwc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Aplwc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with aplwc. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <aplwc.h>
+#include "aplwc_internal.h"
+
+void
+aplwc_voutput(struct aplwc *aplwc, const char *fmt, va_list args)
+{
+ (void)aplwc;
+ vprintf(fmt, args);
+}
diff --git a/po/da.po b/po/da.po
index 791f4de..1c42365 100644
--- a/po/da.po
+++ b/po/da.po
@@ -5,7 +5,7 @@ msgid ""
msgstr ""
"Project-Id-Version: aplwc 0.1.6-fa0c-modified\n"
"Report-Msgid-Bugs-To: bug-aplwc@gnu.org\n"
-"POT-Creation-Date: 2026-04-03 19:30+0200\n"
+"POT-Creation-Date: 2026-04-19 17:07+0200\n"
"PO-Revision-Date: 2026-04-03 14:54+0200\n"
"Last-Translator: <>\n"
"Language-Team: Danish\n"
@@ -16,22 +16,17 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 49.0\n"
-#: src/main.c:68
+#: src/main.c:72
#, c-format
-msgid "You typed: %s\n"
-msgstr "Du skrev: %s\n"
-
-#: src/main.c:71
-#, c-format
-msgid "bye\n"
-msgstr "farvel\n"
+msgid "Bye\n"
+msgstr ""
-#: src/main.c:78
+#: src/main.c:79
#, c-format
msgid "Copyright (C) 2026 Peter Mikkelsen\n"
msgstr ""
-#: src/main.c:79
+#: src/main.c:80
#, c-format
msgid ""
"License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/"
@@ -40,23 +35,46 @@ msgid ""
"There is NO WARRANTY, to the extent permitted by law.\n"
msgstr ""
-#: src/main.c:88
+#: src/main.c:89
#, c-format
msgid ""
"Usage: %s [OPTION...]\n"
"\n"
msgstr "Brug: %s [TILVALG...]\n"
-#: src/main.c:89
+#: src/main.c:90
#, c-format
msgid " -h, --help display this help and exit\n"
msgstr " -h, --help vis denne hjælp og afslut\n"
-#: src/main.c:90
+#: src/main.c:91
#, c-format
msgid " -v, --version display version information and exit\n"
msgstr " -v, --version vis versionsinformation og afslut\n"
+#: src/syscmd.c:56 src/syscmd.c:65 src/syscmd.c:74
+#, c-format
+msgid "Unexpected: %s"
+msgstr ""
+
+#: src/syscmd.c:58
+msgid "Version 0"
+msgstr ""
+
+#: src/syscmd.c:76
+msgid ""
+"Stats...\n"
+"More stats..."
+msgstr ""
+
+#, c-format
+#~ msgid "You typed: %s\n"
+#~ msgstr "Du skrev: %s\n"
+
+#, c-format
+#~ msgid "bye\n"
+#~ msgstr "farvel\n"
+
#, c-format
#~ msgid "Hello from aplwc version '%s'\n"
#~ msgstr "Hej fra aplwc version '%s'\n"
diff --git a/src/main.c b/src/main.c
index f163558..f2764d5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,3 +1,4 @@
+
/* Aplwc - A Programming Language With Constraints
*
* Copyright (C) 2026 Peter Mikkelsen <petermikkelsen10@gmail.com>
@@ -56,19 +57,19 @@ main(int argc, char *argv[])
}
}
- struct aplwc *aplwc = aplwc_new();
+ struct aplwc *aplwc = aplwc_new(malloc, free, realloc);
init_syscmds(aplwc);
init_readline(aplwc);
char *line;
- while(line = readline(" ")){
+ while(aplwc_running(aplwc) && (line = readline(" "))){
if(strlen(line) == 0)
continue;
add_history(line);
- printf(_("You typed: %s\n"), line);
+ aplwc_run_line(aplwc, line);
free(line);
}
- printf(_("bye\n"));
+ printf(_("Bye\n"));
}
static void
diff --git a/src/readline.c b/src/readline.c
index bda4df9..f03bbdb 100644
--- a/src/readline.c
+++ b/src/readline.c
@@ -24,7 +24,6 @@
static struct aplwc *aplwc;
static char **completions;
-static int completion_index;
static char **complete(const char *, int, int);
static char *get_completion(const char *, int);
@@ -52,5 +51,6 @@ complete(const char *text, int start, int end)
static char *
get_completion(const char *text, int state)
{
+ (void)text;
return completions[state];
}
diff --git a/src/syscmd.c b/src/syscmd.c
index 6c3d2e5..8c3a88a 100644
--- a/src/syscmd.c
+++ b/src/syscmd.c
@@ -19,18 +19,59 @@
*/
#include <config.h>
+#include <string.h>
#include "syscmd.h"
+#include "nls.h"
+
+static void syscmd_version(struct aplwc *, struct aplwc_syscmd *, char *);
+static void syscmd_exit(struct aplwc *, struct aplwc_syscmd *, char *);
+static void syscmd_stats(struct aplwc *, struct aplwc_syscmd *, char *);
static struct aplwc_syscmd syscmds[] = {
- {.name = "help"},
- {.name = "version"},
- {.name = "reset"},
- {.name = "exit"},
+ {
+ .name = "version",
+ .run = syscmd_version
+ },
+ {
+ .name = "exit",
+ .run = syscmd_exit
+ },
+ {
+ .name = "stats",
+ .run = syscmd_stats
+ },
};
void
init_syscmds(struct aplwc *aplwc)
{
- for(int i = 0; i < (sizeof(syscmds)/sizeof(*syscmds)); i++)
+ for(size_t i = 0; i < (sizeof(syscmds)/sizeof(*syscmds)); i++)
aplwc_register_syscmd(aplwc, &syscmds[i]);
}
+
+static void
+syscmd_version(struct aplwc *aplwc, struct aplwc_syscmd *syscmd, char *input)
+{
+ if(strlen(input) > 0)
+ aplwc_syscmd_error(aplwc, syscmd, _("Unexpected: %s"));
+ else
+ aplwc_syscmd_output(aplwc, syscmd, _("Version 0"));
+}
+
+static void
+syscmd_exit(struct aplwc *aplwc, struct aplwc_syscmd *syscmd, char *input)
+{
+ if(strlen(input) > 0)
+ aplwc_syscmd_error(aplwc, syscmd, _("Unexpected: %s"));
+ else
+ aplwc_exit(aplwc);
+}
+
+static void
+syscmd_stats(struct aplwc *aplwc, struct aplwc_syscmd *syscmd, char *input)
+{
+ if(strlen(input) > 0)
+ aplwc_syscmd_error(aplwc, syscmd, _("Unexpected: %s"));
+ else
+ aplwc_syscmd_output(aplwc, syscmd, _("Stats...\nMore stats..."));
+}