From 3511f8bc6131b441cd9166c27c3f6abe281e5638 Mon Sep 17 00:00:00 2001 From: dtb Date: Wed, 21 Dec 2022 00:12:26 -0500 Subject: [PATCH] more stuff --- Makefile | 11 +-- README.md | 136 ++++++++++++++++++++++++++---------- src/libblang.c | 78 +++++++++++++++++++++ src/{blang.h => libblang.h} | 11 ++- src/{blang.c => main.c} | 13 ++-- src/ops.c | 112 ----------------------------- src/ops.h | 4 -- 7 files changed, 197 insertions(+), 168 deletions(-) create mode 100644 src/libblang.c rename src/{blang.h => libblang.h} (62%) rename src/{blang.c => main.c} (58%) delete mode 100644 src/ops.c delete mode 100644 src/ops.h diff --git a/Makefile b/Makefile index 3b07632..3d8fe57 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ CC = cc +CFLAGS = -g RM = rm -f TARGETS = bin/blang @@ -10,13 +11,13 @@ clean: build: mkdir -p build -build/blang.o: build src/blang.c src/blang.h - $(CC) -c -o $@ src/blang.c +build/libblang.o: build src/libblang.c src/libblang.h + $(CC) $(CFLAGS) -c -o $@ src/libblang.c -build/ops.o: build src/blang.h src/ops.c src/ops.h - $(CC) -c -o $@ src/ops.c +build/main.o: build src/main.c + $(CC) $(CFLAGS) -c -o $@ src/main.c -bin/blang: build/blang.o build/ops.o +bin/blang: build/libblang.o build/main.o $(CC) $(CFLAGS) -o $@ build/*.o .PHONY: all clean diff --git a/README.md b/README.md index eef1149..72d3f51 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ the bang language Public domain. 2022 DTB. +Bang language is an unpleasant toy designed to be used in the murderu.us IRC +server, but easily adapted anywhere due to its simplicity both in terms of +model and implementation. + ## Glossary ### chart @@ -23,6 +27,15 @@ This can be modified during runtime. A character that indicates what the bang language executor should do next. +### comment + +Ornamental characters, i.e. in-code notes. + +Comments can start with `';'` (in which case they're discarded from the chart) +or `'#'` (in which case they're kept on the chart). + +Comments end with `'!'` or `'\n'`. + ### hand The general-purpose register. @@ -39,7 +52,15 @@ To set a value to the contents of the hand. All operations are one character long. When an unknown operation is executed the resulting behavior is undefined -(and maliciously so; for example, execution can stop or garbage can be written to the chart). +(and maliciously so; for example, execution can stop or garbage can be written +to the chart). + +Operation mappings are based on ergonomics and history. +`'&'` and `'*'` are taken from C. +`'+'` and `'-'` are taken from Brainfuck. +`'<'` and `'>'` are more or less taken from the Bourne shell. +The general ergonomics of bang language are informed by the story of Mel, +part of hacker folklore. ### `'!'` @@ -47,34 +68,47 @@ No-op. Does nothing. `' '`, `'\n'`, `'\r'`, `'\t'`, and `'\v'` are all also no-ops \- keep this in mind when styling code that contains `'?'`! +### `'#'` + +Denotes a comment. +The contents of the comment are kept in the chart but not executed. + ### `';'` Denotes a comment. -A comment starts with a semicolon (or `'#'`, for compatibility with shebangs) -and ends with a bang (`'!'`) or line feed (`'\n'`). +The contents of the comment are discarded from the chart before runtime. -### `'%'` +### `'^'` and `'%'` -Throws at the current location in the program listing to which chart points. +`'^'` palms the content of the location of the program to which chart points. +`'%'` throws at that location. -### `'^'` - -Catches the constant `0`. +The following program prints "Hello, world!", +by iterating over the program listing itself and terminating after printing a +line feed (10 decimal in ASCII): +``` +!Hello, world! +{ +&+* +^> +----------?}; subtract 10, if the value is non-zero return +vv* +``` ### `'&'` and `'*'` -`'&'` (ampersand) palms the chart. `'*'` (splat) throws the chart. +`'&'` (et) palms the chart. `'*'` (splat) throws the chart. -The following program rewrites itself during runtime using ampersand and splat: +The following program rewrites itself during runtime, navigating the chart +using et and splat. Keep in mind `'!'` must be followed by `';'` in comments +to maintain chartlessness: ``` -^{?>!&++*%&+*<^%; set return point; if hand is non-zero, print hand -!&++*; palm the chart; add 2 to palm; splat the new chart -%; write '>' to the new chart position &+*; advance the chart one space -<^%; write '^' to the new chart position -< ->^*%&+*<^%; print hand < >; palm `'\n'`; print hand -^*; splat zero to chart +vv*; splat zero to chart The rest of the program isn't executed. ``` -## Examples +### `'<'` and `'>'` + +`'<'` palms the next character literal. + +`'>'` prints the literal contents of hand to standard output. + +The following program prints "Hello, world!" the easy way: -### true(1) ``` -; initialize hand to 0 -^ - -; set the chart pointer to 0 - this destroys the chart -; and the program exits with the value of the hand -* +!<,>< >v++++++++++>v* ``` -### Hello world: +This is a quine; a program that prints itself. + ``` -; the <.> construct uses '<' to literally 'palm' the next value -; (store into hand), does so, and uses '>' to 'toss' (output hand's value) -<,>< >< ->^* +{&+*!^>v?}v* +``` + +The following program prints the printable ASCII space: + +``` +!^*{; palm bang (the first printable ASCII char), splat +&>+*; et, throw onto screen, increment, splat +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------ +------; subtract 126 +v?}; if hand == 0, return +< +>; print newline +vv*; exit ``` diff --git a/src/libblang.c b/src/libblang.c new file mode 100644 index 0000000..ccab9dc --- /dev/null +++ b/src/libblang.c @@ -0,0 +1,78 @@ +#include +#include +#include + +#include "libblang.h" + +/* assumes unmodified chart */ +void Chart_iron(struct State *s){ + chart_t index; + chart_t lookahead; + + index = s->chart; + while(*index != '\0'){ + if(*index == ';'){ + lookahead = index; + while( + *lookahead != '!' + && *lookahead != '\n' + && *lookahead != '\0' + ) + ++lookahead; + } + ++index; + } +} + +void Ops_bang(struct State *s){ return; } /* more like a whimper */ +void Ops_land(struct State *s){ return; } + +void Ops_snd(struct State *s) { *(s->chart) = s->hand; } +void Ops_rcv(struct State *s) { s->hand = (hand_t)*(s->chart); } + +void Ops_et(struct State *s) { s->hand = (hand_t)s->chart; } +void Ops_splat(struct State *s) { s->chart = (chart_t)s->hand; } + +void Ops_inc(struct State *s) { ++(s->hand); } +void Ops_dec(struct State *s) { --(s->hand); } +void Ops_invert(struct State *s){ s->hand = !s->hand; } + +void Ops_sail(struct State *s) { + while(Ops_lookup(*(s->counter)) != Ops_land) + --*(s->counter); +} + +void Ops_in(struct State *s) { s->hand = *++s->counter; } +void Ops_out(struct State *s) { putc((char)s->hand, stdout); } + +void Ops_what(struct State *s) { s->counter += !s->hand; } + +void Ops_comment(struct State *s){ + while(strchr(BLANG_COMMENT_END, *++s->counter) == NULL); +} + +const struct { + unsigned char name; + void (*f)(struct State *); +} OPS[] = { + { '!', Ops_bang }, { ' ', Ops_bang }, { '\n', Ops_bang }, + { '\r', Ops_bang }, { '\t', Ops_bang }, { '\v', Ops_bang }, + { '%', Ops_snd }, { '^', Ops_rcv }, + { '&', Ops_et }, { '*', Ops_splat }, + { '+', Ops_inc }, { '-', Ops_dec }, { 'v', Ops_invert }, + { '{', Ops_land }, { '}', Ops_sail }, + { '<', Ops_in }, { '>', Ops_out }, + { '?', Ops_what }, + { ';', Ops_comment }, { '#', Ops_comment } +}; + +/* Slower than a switch, but you don't have to update it when you add new + * ops! */ +void (*Ops_lookup(char op))(struct State *){ + size_t i; + + for(i = 0; i < (sizeof OPS)/(sizeof *OPS); ++i) + if(op == OPS[i].name) + return OPS[i].f; + return NULL; +} diff --git a/src/blang.h b/src/libblang.h similarity index 62% rename from src/blang.h rename to src/libblang.h index 1179111..5e378cf 100644 --- a/src/blang.h +++ b/src/libblang.h @@ -1,12 +1,12 @@ -#if !defined _BLANG_H -# define _BLANG_H +#if !defined _LIBBLANG_H +# define _LIBBLANG_H 1 # include /* This has to be big enough to hold a char * without degradation. * Adjust to architecture/system/environment/etc. */ typedef uint64_t hand_t; /* Holds *argv; will not change between environments. */ -typedef char * chart_t; +typedef char *chart_t; struct State{ chart_t chart; @@ -14,4 +14,9 @@ struct State{ chart_t counter; hand_t hand; }; + +/* Characters that end a comment. */ +#define BLANG_COMMENT_END "!\n" + +void (*Ops_lookup(char op))(struct State *); #endif diff --git a/src/blang.c b/src/main.c similarity index 58% rename from src/blang.c rename to src/main.c index 88a48ef..fa0813e 100644 --- a/src/blang.c +++ b/src/main.c @@ -2,23 +2,22 @@ #include #include -#include "blang.h" -#include "ops.h" +#include "libblang.h" int main(int argc, char **argv){ struct State s; int c; void (*op)(struct State *); - s.chart = *(argv+1); + s.chart = argv[1]; s.counter = s.chart; - for(s.counter = s.chart; ; ++s.counter){ + for(s.counter = s.chart; *s.counter != '!'; ++s.counter); + + for(;; ++s.counter){ if(s.chart == (char *)0) return (int)s.hand; - if((op = Ops_lookup(*s.counter)) == NULL) - return 127; - else + if((op = Ops_lookup(*s.counter)) != NULL) op(&s); } } diff --git a/src/ops.c b/src/ops.c deleted file mode 100644 index 998edc1..0000000 --- a/src/ops.c +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include - -#include "blang.h" - -/* no-op */ -void Ops_bang(struct State *s){ - return; -} - -void Ops_percent(struct State *s){ - *(s->chart) = s->hand; -} - -void Ops_carat(struct State *s){ - s->hand = 0; -} - -void Ops_ampersand(struct State *s){ - s->hand = (uint64_t)s->chart; -} - -void Ops_splat(struct State *s){ - s->chart = (char *)s->hand; -} - -void Ops_plus(struct State *s){ - ++(s->hand); -} - -void Ops_dash(struct State *s){ - --(s->hand); -} - -void Ops_lbrack(struct State *s){ - s->point = s->counter; -} - -void Ops_rbrack(struct State *s){ - s->counter = s->point; -} - -void Ops_left(struct State *s){ - s->hand = *++s->counter; -} - -void Ops_right(struct State *s){ - putc((char)s->hand, stdout); -} - -void Ops_what(struct State *s){ - if(!s->hand) - ++(s->counter); -} - -void Ops_semi(struct State *s){ - int c; - - while(strchr("!\n", *++s->counter) == NULL); -} - -void Ops_mirror(struct State *s){ - s->hand = !s->hand; -} - -void Ops_cross(struct State *s){ - char *i; - i = s->chart; - s->chart = (char *)s->hand; - s->hand = (uint64_t)i; -} - -const struct { - unsigned char name; - void (*f)(struct State *); -} OPS[] = { - { '#', Ops_semi }, - { '%', Ops_percent }, - { '^', Ops_carat }, - { '&', Ops_ampersand }, - { '*', Ops_splat }, - { '+', Ops_plus }, - { '-', Ops_dash }, - { '{', Ops_lbrack }, - { '}', Ops_rbrack }, - { ';', Ops_semi }, - { '>', Ops_right }, - { '<', Ops_left }, - { '?', Ops_what }, - { 'v', Ops_mirror }, - { 'x', Ops_cross }, - - /* no-ops */ - { '!', Ops_bang }, - { ' ', Ops_bang }, - { '\n', Ops_bang }, - { '\r', Ops_bang }, - { '\t', Ops_bang }, - { '\v', Ops_bang } -}; - -/* Slower than a switch but you don't have to update it when you add new - * ops! */ -void (*Ops_lookup(char op))(struct State *){ - size_t i; - - for(i = 0; i < (sizeof OPS)/(sizeof *OPS); ++i) - if(op == OPS[i].name) - return OPS[i].f; - return NULL; -} diff --git a/src/ops.h b/src/ops.h deleted file mode 100644 index a318965..0000000 --- a/src/ops.h +++ /dev/null @@ -1,4 +0,0 @@ -#if !defined _BLANG_OPS -# define _BLANG_OPS 1 -void (*Ops_lookup(char op))(struct State *); -#endif