Compare commits

..

7 Commits

Author SHA1 Message Date
dtb
3511f8bc61 more stuff 2022-12-21 00:12:26 -05:00
dtb
adacc89143 more reference 2022-12-10 22:56:17 -05:00
dtb
2e9cd20761 mirror and brackets 2022-12-09 21:59:48 -05:00
dtb
193254fb1f more doc 2022-12-09 21:59:24 -05:00
dtb
6a90901f81 blangfile(1) 2022-12-09 21:34:03 -05:00
dtb
6c659ee621 add more readme stuff 2022-12-09 21:05:38 -05:00
dtb
35a3fb0d62 build, build/* 2022-12-09 21:03:23 -05:00
12 changed files with 296 additions and 131 deletions

5
.gitignore vendored
View File

@@ -1,2 +1,3 @@
bin bin/blang
bin/* build
build/*

View File

@@ -1,4 +1,5 @@
CC = cc CC = cc
CFLAGS = -g
RM = rm -f RM = rm -f
TARGETS = bin/blang TARGETS = bin/blang
@@ -7,18 +8,16 @@ all: $(TARGETS)
clean: clean:
$(RM) $(TARGETS) $(RM) $(TARGETS)
bin:
mkdir -p bin
build: build:
mkdir -p build mkdir -p build
build/blang.o: build src/blang.c src/blang.h build/libblang.o: build src/libblang.c src/libblang.h
$(CC) -c -o $@ src/blang.c $(CC) $(CFLAGS) -c -o $@ src/libblang.c
build/ops.o: build src/blang.h src/ops.c src/ops.h build/main.o: build src/main.c
$(CC) -c -o $@ src/ops.c $(CC) $(CFLAGS) -c -o $@ src/main.c
bin/blang: bin build/blang.o build/ops.o bin/blang: build/libblang.o build/main.o
$(CC) $(CFLAGS) -o $@ build/*.o $(CC) $(CFLAGS) -o $@ build/*.o
.PHONY: all clean .PHONY: all clean

178
README.md
View File

@@ -12,20 +12,176 @@ the bang language
Public domain. 2022 DTB. Public domain. 2022 DTB.
# Examples 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.
## true(1) ## Glossary
### chart
The listing of the program currently running.
This can be modified during runtime.
### command
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.
### catch
To set the hand to a value.
### throw
To set a value to the contents of the hand.
## Reference
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).
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.
### `'!'`
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.
The contents of the comment are discarded from the chart before runtime.
### `'^'` and `'%'`
`'^'` palms the content of the location of the program to which chart points.
`'%'` throws at that location.
The following program prints "Hello, world!",
by iterating over the program listing itself and terminating after printing a
line feed (10 decimal in ASCII):
``` ```
^ ; initialize hand to 0 !Hello, world!
; set the chart pointer to 0 - this destroys the chart {
; and the program exits with the value of the hand &+*
* ^>
----------?}; subtract 10, if the value is non-zero return
vv*
``` ```
## Hello world: ### `'&'` and `'*'`
`'&'` (et) palms the chart. `'*'` (splat) throws the chart.
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:
``` ```
; the <.> construct uses '<' to store the next value literally into state, !vv; zero out hand
; does so, and uses '>' to output state literally {?>; set return point; if hand is non-zero, print hand
<H><e><l><l><o><,>< ><w><o><r><l><d><!>< !&; 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
<
%; write '\n' to the new chart position
&+*; advance the chart one space
<>%; write '>' to the new chart position
&+*; advance the chart one space
<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:
```
!vv; zero out hand
{!>; print hand
<
>; palm `'\n'`; print hand
vv*; splat zero to chart
The rest of the program isn't executed.
```
### `'<'` and `'>'`
`'<'` palms the next character literal.
`'>'` prints the literal contents of hand to standard output.
The following program prints "Hello, world!" the easy way:
```
!<H><e><l><l><o><,>< ><w><o><r><l><d><!>v++++++++++>v*
```
This is a quine; a program that prints itself.
```
{&+*!^>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
``` ```

3
bin/blangfile Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
blang "$(cat "$@")"

View File

@@ -0,0 +1,2 @@
<H><e><l><l><o><,>< ><w><o><r><l><d><!><
>^*

View File

@@ -0,0 +1,18 @@
^{?>!&++*<!%&++*<<%&+*<
%&+*<>%&+*<^%<!}
^; 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 2 to palm; splat the new chart
<<%; write '<' to the new chart position
&+*; advance the chart one space
<
%; write '\n' to the new chart position
&+*; advance the chart one space
<>%; write '>' to the new chart position
&+*; advance the chart one space
<^%; write '^' to the new chart position
<!; palm '!'
}; return

2
example/true.blang Normal file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env blangfile
^*

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,19 +1,22 @@
#if !defined _BLANG_H #if !defined _LIBBLANG_H
# define _BLANG_H # define _LIBBLANG_H 1
# include <stdint.h> # include <stdint.h>
/* This has to be big enough to hold a char * without degradation. /* This has to be big enough to hold a char * without degradation.
* Adjust to architecture/system/environment/etc. */ * Adjust to architecture/system/environment/etc. */
typedef uint64_t hand_t; typedef uint64_t hand_t;
/* Just has to be fairly big. (TODO specify) */
typedef uint64_t counter_t;
/* Holds *argv; will not change between environments. */ /* Holds *argv; will not change between environments. */
typedef char *chart_t; typedef char *chart_t;
struct State{ struct State{
chart_t chart; chart_t chart;
counter_t counter; chart_t point;
chart_t counter;
hand_t hand; hand_t hand;
}; };
/* Characters that end a comment. */
#define BLANG_COMMENT_END "!\n"
void (*Ops_lookup(char op))(struct State *);
#endif #endif

View File

@@ -2,23 +2,22 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "blang.h" #include "libblang.h"
#include "ops.h"
int main(int argc, char **argv){ int main(int argc, char **argv){
struct State s; struct State s;
int c; int c;
void (*op)(struct State *); void (*op)(struct State *);
s.chart = *(argv+1); s.chart = argv[1];
s.counter = 0; s.counter = s.chart;
for(s.counter = 0; ; ++s.counter){ for(s.counter = s.chart; *s.counter != '!'; ++s.counter);
for(;; ++s.counter){
if(s.chart == (char *)0) if(s.chart == (char *)0)
return (int)s.hand; return (int)s.hand;
if((op = Ops_lookup(s.chart[s.counter])) == NULL) if((op = Ops_lookup(*s.counter)) != NULL)
return 127;
else
op(&s); op(&s);
} }
} }

View File

@@ -1,92 +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 = (char)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_right(struct State *s){
putc((char)s->hand, stdout);
}
void Ops_left(struct State *s){
s->hand = getc(stdin);
}
void Ops_semi(struct State *s){
int c;
while(
(c = getc(stdin)) != EOF
&& strchr("!\n", c) == NULL
);
}
void Ops_cross(struct State *s){
char *i;
i = s->chart;
s->chart = (char *)s->hand;
s->hand = (unsigned char)i;
}
const struct {
unsigned char name;
void (*f)(struct State *);
} OPS[] = {
{ '%', Ops_percent },
{ '^', Ops_carat },
{ '&', Ops_ampersand },
{ '*', Ops_splat },
{ '+', Ops_plus },
{ '-', Ops_dash },
{ '>', Ops_right },
{ '<', Ops_left },
{ '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