more stuff
This commit is contained in:
parent
adacc89143
commit
3511f8bc61
11
Makefile
11
Makefile
@ -1,4 +1,5 @@
|
|||||||
CC = cc
|
CC = cc
|
||||||
|
CFLAGS = -g
|
||||||
RM = rm -f
|
RM = rm -f
|
||||||
TARGETS = bin/blang
|
TARGETS = bin/blang
|
||||||
|
|
||||||
@ -10,13 +11,13 @@ clean:
|
|||||||
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: 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
|
||||||
|
136
README.md
136
README.md
@ -12,6 +12,10 @@ the bang language
|
|||||||
|
|
||||||
Public domain. 2022 DTB.
|
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
|
## Glossary
|
||||||
|
|
||||||
### chart
|
### chart
|
||||||
@ -23,6 +27,15 @@ This can be modified during runtime.
|
|||||||
|
|
||||||
A character that indicates what the bang language executor should do next.
|
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
|
### hand
|
||||||
|
|
||||||
The general-purpose register.
|
The general-purpose register.
|
||||||
@ -39,7 +52,15 @@ To set a value to the contents of the hand.
|
|||||||
|
|
||||||
All operations are one character long.
|
All operations are one character long.
|
||||||
When an unknown operation is executed the resulting behavior is undefined
|
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
|
`' '`, `'\n'`, `'\r'`, `'\t'`, and `'\v'` are all also no-ops
|
||||||
\- keep this in mind when styling code that contains `'?'`!
|
\- 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.
|
Denotes a comment.
|
||||||
A comment starts with a semicolon (or `'#'`, for compatibility with shebangs)
|
The contents of the comment are discarded from the chart before runtime.
|
||||||
and ends with a bang (`'!'`) or line feed (`'\n'`).
|
|
||||||
|
|
||||||
### `'%'`
|
### `'^'` 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.
|
||||||
|
|
||||||
### `'^'`
|
The following program prints "Hello, world!",
|
||||||
|
by iterating over the program listing itself and terminating after printing a
|
||||||
Catches the constant `0`.
|
line feed (10 decimal in ASCII):
|
||||||
|
```
|
||||||
|
!Hello, world!
|
||||||
|
{
|
||||||
|
&+*
|
||||||
|
^>
|
||||||
|
----------?}; subtract 10, if the value is non-zero return
|
||||||
|
vv*
|
||||||
|
```
|
||||||
|
|
||||||
### `'&'` and `'*'`
|
### `'&'` 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:
|
||||||
|
|
||||||
```
|
```
|
||||||
^{?>!&++*<!%&++*<<%&+*<
|
!vv; zero out hand
|
||||||
%&+*<>%&+*<^%<!}
|
|
||||||
|
|
||||||
^; zero out hand
|
|
||||||
{?>; set return point; if hand is non-zero, print hand
|
{?>; set return point; if hand is non-zero, print hand
|
||||||
!&++*; palm the chart; add 2 to palm; splat the new chart
|
!&; palm the chart
|
||||||
<!%; write '!' to the new chart position
|
++++*; 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
|
&++*; palm the chart; add 2 to palm; splat the new chart
|
||||||
<<%; write '<' to the new chart position
|
<<%; write '<' to the new chart position
|
||||||
&+*; advance the chart one space
|
&+*; 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
|
&+*; advance the chart one space
|
||||||
<>%; write '>' to the new chart position
|
<>%; write '>' to the new chart position
|
||||||
&+*; advance the chart one space
|
&+*; advance the chart one space
|
||||||
<^%; write '^' to the new chart position
|
<v%; write 'v' to the new chart position
|
||||||
<!; palm '!'
|
&+*; advance the chart one space
|
||||||
|
<v%; write 'v' to the new chart position
|
||||||
|
<!; palm '!;'
|
||||||
}; return
|
}; return
|
||||||
```
|
```
|
||||||
|
|
||||||
Right before the final `'}'`, the chart is the following:
|
Right before the final `'}'`, the chart is the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
^{!><
|
!vv; zero out hand
|
||||||
>^*<!%&++*<<%&+*<
|
|
||||||
%&+*<>%&+*<^%<!}
|
|
||||||
|
|
||||||
^; zero out hand
|
|
||||||
{!>; print hand
|
{!>; print hand
|
||||||
<
|
<
|
||||||
>; palm `'\n'`; print hand
|
>; palm `'\n'`; print hand
|
||||||
^*; splat zero to chart
|
vv*; splat zero to chart
|
||||||
|
|
||||||
The rest of the program isn't executed.
|
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
|
!<H><e><l><l><o><,>< ><w><o><r><l><d><!>v++++++++++>v*
|
||||||
^
|
|
||||||
|
|
||||||
; set the chart pointer to 0 - this destroys the chart
|
|
||||||
; and the program exits with the value of the hand
|
|
||||||
*
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Hello world:
|
This is a quine; a program that prints itself.
|
||||||
|
|
||||||
```
|
```
|
||||||
; the <.> construct uses '<' to literally 'palm' the next value
|
{&+*!^>v?}v*
|
||||||
; (store into hand), does so, and uses '>' to 'toss' (output hand's value)
|
```
|
||||||
<H><e><l><l><o><,>< ><w><o><r><l><d><!><
|
|
||||||
>^*
|
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
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,5 +1,5 @@
|
|||||||
#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. */
|
||||||
@ -14,4 +14,9 @@ struct State{
|
|||||||
chart_t counter;
|
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 = s.chart;
|
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)
|
if(s.chart == (char *)0)
|
||||||
return (int)s.hand;
|
return (int)s.hand;
|
||||||
if((op = Ops_lookup(*s.counter)) == NULL)
|
if((op = Ops_lookup(*s.counter)) != NULL)
|
||||||
return 127;
|
|
||||||
else
|
|
||||||
op(&s);
|
op(&s);
|
||||||
}
|
}
|
||||||
}
|
}
|
112
src/ops.c
112
src/ops.c
@ -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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user