Compare commits
14 Commits
c692ebef67
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3511f8bc61 | |||
| adacc89143 | |||
| 2e9cd20761 | |||
| 193254fb1f | |||
| 6a90901f81 | |||
| 6c659ee621 | |||
| 35a3fb0d62 | |||
| add3346ce9 | |||
| da0ec7de01 | |||
| 905a0e9056 | |||
| efa6fe674f | |||
| 92f13c8e30 | |||
| 721b556647 | |||
| 505fb17779 |
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
bin/blang
|
||||||
|
build
|
||||||
|
build/*
|
||||||
23
Makefile
Normal file
23
Makefile
Normal 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
179
README.md
@@ -12,21 +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 state to 0
|
!Hello, world!
|
||||||
% ; 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
|
^>
|
||||||
$
|
----------?}; 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
3
bin/blangfile
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
blang "$(cat "$@")"
|
||||||
42
blang.c
42
blang.c
@@ -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;
|
|
||||||
}
|
|
||||||
2
example/hello_world.blang
Normal file
2
example/hello_world.blang
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<H><e><l><l><o><,>< ><w><o><r><l><d><!><
|
||||||
|
>^*
|
||||||
18
example/meta_programming.blang
Normal file
18
example/meta_programming.blang
Normal 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
2
example/true.blang
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env blangfile
|
||||||
|
^*
|
||||||
78
src/libblang.c
Normal file
78
src/libblang.c
Normal 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
22
src/libblang.h
Normal 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
23
src/main.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user