Compare commits
7 Commits
add3346ce9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3511f8bc61 | |||
| adacc89143 | |||
| 2e9cd20761 | |||
| 193254fb1f | |||
| 6a90901f81 | |||
| 6c659ee621 | |||
| 35a3fb0d62 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
bin
|
bin/blang
|
||||||
bin/*
|
build
|
||||||
|
build/*
|
||||||
|
|||||||
13
Makefile
13
Makefile
@@ -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
178
README.md
@@ -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
3
bin/blangfile
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
blang "$(cat "$@")"
|
||||||
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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
92
src/ops.c
92
src/ops.c
@@ -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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user