Compare commits

...

14 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
dtb
add3346ce9 get hello world working again, fix type issues 2022-12-09 21:03:06 -05:00
dtb
da0ec7de01 change blang op meanings 2022-12-09 19:46:32 -05:00
dtb
905a0e9056 fix most compiler errors 2022-12-09 19:26:10 -05:00
dtb
efa6fe674f modular ops (untested) 2022-12-09 16:33:04 -05:00
dtb
92f13c8e30 bin, bin/* 2022-12-09 00:09:40 -05:00
dtb
721b556647 now works 2022-12-08 23:51:40 -05:00
dtb
505fb17779 now compiles with GNU 2022-12-08 23:51:28 -05:00
11 changed files with 341 additions and 54 deletions

3
.gitignore vendored Normal file
View File

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

23
Makefile Normal file
View File

@@ -0,0 +1,23 @@
CC = cc
CFLAGS = -g
RM = rm -f
TARGETS = bin/blang
all: $(TARGETS)
clean:
$(RM) $(TARGETS)
build:
mkdir -p build
build/libblang.o: build src/libblang.c src/libblang.h
$(CC) $(CFLAGS) -c -o $@ src/libblang.c
build/main.o: build src/main.c
$(CC) $(CFLAGS) -c -o $@ src/main.c
bin/blang: build/libblang.o build/main.o
$(CC) $(CFLAGS) -o $@ build/*.o
.PHONY: all clean

179
README.md
View File

@@ -12,21 +12,176 @@ the bang language
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 state to 0
% ; store state (0) into *stack
; stack is destroyed, state becomes the final value of *stack
; when the stack is destroyed, the program exits with the value of state
$
!Hello, world!
{
&+*
^>
----------?}; 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,
; does so, and uses '>' to output state literally
<H><e><l><l><o><,>< ><w><o><r><l><d><!><
>^%$
!vv; zero out hand
{?>; set return point; if hand is non-zero, print hand
!&; 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 "$@")"

42
blang.c
View File

@@ -1,42 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
static unsigned char get = 0;
static unsigned char _stack[4];
static unsigned char *stack = _stack;
static unsigned char *state;
int main(){
int c;
while((c = getchar()) != EOF){
if(stack == (unsigned char *)0)
return (char)state;
if(get){
state = (char *)c;
get = 0;
continue;
}
switch(c){
case ' ':
case '\n':
case '\r':
case '\t':
case '\v':
break;
case '!': state = stack; break;
case '$': stack = state; state = (char *)*_stack; break;
case '%': *stack = (char)state; break;
case '^': state = (char *)0; break;
case '+': ++(char)state; break;
case '-': --(char)state; break;
case '>': putchar((char)state); break;
case '<': get = 1; break;
case ':':
while((c = getchar()) != '\n')
if(c == EOF)
goto fin;
break;
}
}
fin: return 1;
}

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;
}

22
src/libblang.h Normal file
View File

@@ -0,0 +1,22 @@
#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;
struct State{
chart_t chart;
chart_t point;
chart_t counter;
hand_t hand;
};
/* Characters that end a comment. */
#define BLANG_COMMENT_END "!\n"
void (*Ops_lookup(char op))(struct State *);
#endif

23
src/main.c Normal file
View File

@@ -0,0 +1,23 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "libblang.h"
int main(int argc, char **argv){
struct State s;
int c;
void (*op)(struct State *);
s.chart = argv[1];
s.counter = s.chart;
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)
op(&s);
}
}