more stuff
This commit is contained in:
parent
adacc89143
commit
3511f8bc61
11
Makefile
11
Makefile
@ -1,4 +1,5 @@
|
||||
CC = cc
|
||||
CFLAGS = -g
|
||||
RM = rm -f
|
||||
TARGETS = bin/blang
|
||||
|
||||
@ -10,13 +11,13 @@ clean:
|
||||
build:
|
||||
mkdir -p build
|
||||
|
||||
build/blang.o: build src/blang.c src/blang.h
|
||||
$(CC) -c -o $@ src/blang.c
|
||||
build/libblang.o: build src/libblang.c src/libblang.h
|
||||
$(CC) $(CFLAGS) -c -o $@ src/libblang.c
|
||||
|
||||
build/ops.o: build src/blang.h src/ops.c src/ops.h
|
||||
$(CC) -c -o $@ src/ops.c
|
||||
build/main.o: build src/main.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
|
||||
|
||||
.PHONY: all clean
|
||||
|
136
README.md
136
README.md
@ -12,6 +12,10 @@ the bang language
|
||||
|
||||
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
|
||||
|
||||
### chart
|
||||
@ -23,6 +27,15 @@ This can be modified during runtime.
|
||||
|
||||
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.
|
||||
@ -39,7 +52,15 @@ To set a value to the contents of the hand.
|
||||
|
||||
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).
|
||||
(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
|
||||
\- 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.
|
||||
A comment starts with a semicolon (or `'#'`, for compatibility with shebangs)
|
||||
and ends with a bang (`'!'`) or line feed (`'\n'`).
|
||||
The contents of the comment are discarded from the chart before runtime.
|
||||
|
||||
### `'%'`
|
||||
### `'^'` 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.
|
||||
|
||||
### `'^'`
|
||||
|
||||
Catches the constant `0`.
|
||||
The following program prints "Hello, world!",
|
||||
by iterating over the program listing itself and terminating after printing a
|
||||
line feed (10 decimal in ASCII):
|
||||
```
|
||||
!Hello, world!
|
||||
{
|
||||
&+*
|
||||
^>
|
||||
----------?}; subtract 10, if the value is non-zero return
|
||||
vv*
|
||||
```
|
||||
|
||||
### `'&'` 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:
|
||||
|
||||
```
|
||||
^{?>!&++*<!%&++*<<%&+*<
|
||||
%&+*<>%&+*<^%<!}
|
||||
|
||||
^; zero out hand
|
||||
!vv; 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 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
|
||||
@ -83,43 +117,71 @@ The following program rewrites itself during runtime using ampersand and splat:
|
||||
&+*; advance the chart one space
|
||||
<>%; write '>' to the new chart position
|
||||
&+*; advance the chart one space
|
||||
<^%; write '^' to the new chart position
|
||||
<!; palm '!'
|
||||
<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:
|
||||
|
||||
```
|
||||
^{!><
|
||||
>^*<!%&++*<<%&+*<
|
||||
%&+*<>%&+*<^%<!}
|
||||
|
||||
^; zero out hand
|
||||
!vv; zero out hand
|
||||
{!>; print hand
|
||||
<
|
||||
>; palm `'\n'`; print hand
|
||||
^*; splat zero to chart
|
||||
vv*; splat zero to chart
|
||||
|
||||
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
|
||||
^
|
||||
|
||||
; set the chart pointer to 0 - this destroys the chart
|
||||
; and the program exits with the value of the hand
|
||||
*
|
||||
!<H><e><l><l><o><,>< ><w><o><r><l><d><!>v++++++++++>v*
|
||||
```
|
||||
|
||||
### Hello world:
|
||||
This is a quine; a program that prints itself.
|
||||
|
||||
```
|
||||
; the <.> construct uses '<' to literally 'palm' the next value
|
||||
; (store into hand), does so, and uses '>' to 'toss' (output hand's value)
|
||||
<H><e><l><l><o><,>< ><w><o><r><l><d><!><
|
||||
>^*
|
||||
{&+*!^>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
|
||||
```
|
||||
|
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,12 +1,12 @@
|
||||
#if !defined _BLANG_H
|
||||
# define _BLANG_H
|
||||
#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;
|
||||
typedef char *chart_t;
|
||||
|
||||
struct State{
|
||||
chart_t chart;
|
||||
@ -14,4 +14,9 @@ struct State{
|
||||
chart_t counter;
|
||||
hand_t hand;
|
||||
};
|
||||
|
||||
/* Characters that end a comment. */
|
||||
#define BLANG_COMMENT_END "!\n"
|
||||
|
||||
void (*Ops_lookup(char op))(struct State *);
|
||||
#endif
|
@ -2,23 +2,22 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "blang.h"
|
||||
#include "ops.h"
|
||||
#include "libblang.h"
|
||||
|
||||
int main(int argc, char **argv){
|
||||
struct State s;
|
||||
int c;
|
||||
void (*op)(struct State *);
|
||||
|
||||
s.chart = *(argv+1);
|
||||
s.chart = argv[1];
|
||||
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)
|
||||
return (int)s.hand;
|
||||
if((op = Ops_lookup(*s.counter)) == NULL)
|
||||
return 127;
|
||||
else
|
||||
if((op = Ops_lookup(*s.counter)) != NULL)
|
||||
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