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.
|
||||
|
||||
# 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
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