more stuff

This commit is contained in:
dtb 2022-12-21 00:12:26 -05:00
parent adacc89143
commit 3511f8bc61
7 changed files with 197 additions and 168 deletions

View File

@ -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

136
README.md
View File

@ -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:
```
^{?>!&++*<!%&++*<<%&+*<
%&+*<>%&+*<^%<!}
^; zero out hand
!vv; zero out hand
{?>; 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
!&; palm the chart
++++*; add 4 to palm; splat the new chart
<!%; write '!;' to the new chart position
&++*; palm the chart; add 2 to palm; splat the new chart
<<%; write '<' to the new chart position
&+*; advance the chart one space
@ -83,43 +117,71 @@ The following program rewrites itself during runtime using ampersand and splat:
&+*; advance the chart one space
<>%; write '>' to the new chart position
&+*; advance the chart one space
<^%; write '^' to the new chart position
<!; palm '!'
<v%; write 'v' to the new chart position
&+*; advance the chart one space
<v%; write 'v' to the new chart position
<!; palm '!;'
}; return
```
Right before the final `'}'`, the chart is the following:
```
^{!><
>^*<!%&++*<<%&+*<
%&+*<>%&+*<^%<!}
^; zero out hand
!vv; zero out hand
{!>; 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
*
!<H><e><l><l><o><,>< ><w><o><r><l><d><!>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)
<H><e><l><l><o><,>< ><w><o><r><l><d><!><
>^*
{&+*!^>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
```

78
src/libblang.c Normal file
View File

@ -0,0 +1,78 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -1,12 +1,12 @@
#if !defined _BLANG_H
# define _BLANG_H
#if !defined _LIBBLANG_H
# define _LIBBLANG_H 1
# include <stdint.h>
/* 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

View File

@ -2,23 +2,22 @@
#include <stdio.h>
#include <stdlib.h>
#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);
}
}

112
src/ops.c
View File

@ -1,112 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -1,4 +0,0 @@
#if !defined _BLANG_OPS
# define _BLANG_OPS 1
void (*Ops_lookup(char op))(struct State *);
#endif