├── examples ├── cat.tri ├── cat2.tri ├── direction.tri └── placeholder.tri ├── .gitignore ├── makefile ├── undocumented.md ├── license ├── main.c ├── readme.md └── triangular.c /examples/cat.tri: -------------------------------------------------------------------------------- 1 | \@~/;< 2 | -------------------------------------------------------------------------------- /examples/cat2.tri: -------------------------------------------------------------------------------- 1 | (.~..;)p@< 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | triangular 2 | triangular.exe 3 | 4 | *.o 5 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc -g -o triangular triangular.c main.c 3 | -------------------------------------------------------------------------------- /examples/direction.tri: -------------------------------------------------------------------------------- 1 | , 2 | > \ 3 | , , < 4 | v ` . & 5 | . . \ . . 6 | . > / > ^ . 7 | -------------------------------------------------------------------------------- /examples/placeholder.tri: -------------------------------------------------------------------------------- 1 | . 2 | . . 3 | . . . 4 | . . . . 5 | . . . . . 6 | . . . . . . 7 | -------------------------------------------------------------------------------- /undocumented.md: -------------------------------------------------------------------------------- 1 | Here are some features I don't like, but make the language easier to use: 2 | 3 | ## Extra IP switches 4 | 5 | j conditionally (if ToS > 0) switch to NW ` 6 | k conditionally switch to direction N ^ 7 | y conditionally switch to direction NE / 8 | n conditionally switch to direction E > 9 | q conditionally switch to direction SE \ 10 | r conditionally switch to direction S v 11 | t conditionally switch to direction SW , 12 | w conditionally switch to direction W < 13 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Aaron Ryan Klingler 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* (x*x+x)/2 */ 6 | 7 | char buf[1000][1000]; 8 | int prettyprint, display_code; 9 | 10 | int getmax(int); 11 | void space(int); 12 | void readtri(FILE*,int); 13 | 14 | extern void triangular(void); 15 | 16 | int main(int argc, char **argv) 17 | { 18 | if (argc < 2) 19 | return fprintf(stderr,"Usage: triangular source.tri\n"); 20 | 21 | if (argc > 2) { 22 | int x; 23 | for (x = 2; x < argc; x++) { 24 | if (!strcmp(argv[x],"--pretty") || !strcmp(argv[x],"--prettyprint")) 25 | prettyprint = 1; 26 | else if (!strcmp(argv[x],"--display-code") || !strcmp(argv[x],"--debug") || !strcmp(argv[x],"--step")) 27 | display_code = 1; 28 | } 29 | } 30 | 31 | //if (argc > 2 && !strcmp(argv[2],"--verbose")) 32 | // verbose = 1; 33 | //if (argc > 3 && !strcmp(argv[3],"--display-code")) 34 | // display_code = 1; 35 | 36 | FILE *in = fopen(argv[1],"r"); 37 | 38 | if (!in) 39 | return fprintf(stderr,"Error: Could not open file %s: %s\n",argv[1],strerror(errno)); 40 | 41 | int size = 0, c, i; 42 | while ((c = getc(in)) != EOF) 43 | if (c != ' ' && c != '\n' && c != '\r') 44 | size++; 45 | rewind(in); 46 | 47 | int len = getmax(size) - 1; 48 | int l_cpy = len; 49 | 50 | if (prettyprint) 51 | { 52 | int j; 53 | for (i = 1; l_cpy; l_cpy--, i++) { 54 | space(l_cpy); 55 | j = i; 56 | while (j) { 57 | c = getc(in); 58 | if (c != ' ' && c != '\n' && c != '\r') { 59 | printf("%c ",c); 60 | j--; 61 | } 62 | } 63 | puts(""); 64 | } 65 | puts(""); 66 | } 67 | 68 | rewind(in); 69 | readtri(in,len); 70 | 71 | triangular(); 72 | } 73 | 74 | void space(int s) 75 | { 76 | while (s--) 77 | putchar(' '); 78 | } 79 | 80 | int getmax(int length) 81 | { 82 | int i = 0, res = 0; 83 | while (1) { 84 | if (res >= length) 85 | return i; 86 | res += i++; 87 | } 88 | } 89 | 90 | void readtri(FILE *in, int len) 91 | { 92 | int c, size, vals, x, y, changed; 93 | c = x = y = vals = 0, changed; 94 | 95 | for (size = 1; size <= len; size++) { 96 | changed = 0; 97 | vals = size; 98 | while (vals) { 99 | int c = getc(in); 100 | if (c != ' ' && c != '\n' && c != '\r') { 101 | changed = 1; 102 | buf[y][x++] = c; 103 | vals--; 104 | } 105 | } 106 | if (changed) 107 | y++; 108 | x = changed = 0; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Triangular 2 | 3 | Triangular is a two-dimensional stack-based esoteric programming language, inspired by [Surface](http://esolangs.org/wiki/Surface) and [Hexagony](https://github.com/m-ender/hexagony). 4 | Source code is read in the shape of the smallest triangle that the length of the code can fit into. Whitespace is ignored. 5 | For example, if the source code was `123456`, Triangular would format this into: 6 | 7 | 1 8 | 2 3 9 | 4 5 6 10 | 11 | To build, type `make`. This will generate an executable `triangular`. Command-line options: 12 | 13 | --verbose prettyprint the triangle 14 | --display-code display code as it's read 15 | 16 | Triangular has: 17 | 18 | - A stack size of 30,000 19 | - A maximum of 300 jumps 20 | - A maximum source code of 1000 lines, 1000 characters per line 21 | - A second stack (see section Memory), which is currently just one value. 22 | 23 | Acronyms: **ToS** = top of stack, **IP** = instruction pointer 24 | 25 | There are eight directions in which the IP can move (the IP is located at `.`): 26 | 27 | 2 28 | 1 3 29 | 8 . 4 30 | 7 5 31 | 6 32 | 33 | (`.` is a no-op.) 34 | 35 | The directional numbers correspond to the cardinal directions and their combinations like so: 36 | 37 | 1 NorthWest ` 38 | 2 North ^ 39 | 3 NorthEast / 40 | 4 East > 41 | 5 SouthEast \ 42 | 6 South v 43 | 7 SouthWest , 44 | 8 West < 45 | 46 | Visualization of directional switches: 47 | 48 | ^ 49 | ` / 50 | < . > 51 | , \ 52 | v 53 | 54 | IP starts from the top of the triangle, moving in direction 5 (SE). 55 | 56 | \ 57 | . 58 | . . 59 | . . . 60 | . . . . 61 | . . . . . 62 | 63 | If the IP goes off the playing field, the program will terminate. 64 | 65 | # Commands 66 | 67 | ## IP switches 68 | 69 | ` switch to direction 1 (NW) 70 | ^ switch to direction 2 (N) 71 | / switch to direction 3 (NE) 72 | > switch to direction 4 (E) 73 | \ switch to direction 5 (SE) 74 | v switch to direction 6 (S) 75 | , switch to direction 7 (SW) 76 | < switch to direction 8 (W) 77 | o rotate the direction of the IP clockwise 78 | e rotate the direction of the IP counter-clockwise 79 | c rotate the direction of the IP clockwise, then become z 80 | z rotate the direction of the IP counter-clockwise, then become c 81 | 82 | ## Program commands 83 | 84 | & end program 85 | 86 | ## Stack commands 87 | 88 | p pop 89 | : duplicate ToS 90 | + add ToS to ToS-1, pop both, push result 91 | - subtract ToS from ToS-1, pop both, push result 92 | * multiply ToS and ToS-1 together, pop both, push result 93 | _ divide ToS-1 by ToS, pop both, push result 94 | = compare ToS and ToS-1 for equality, DON'T pop, push result 95 | d decrement ToS 96 | i increment ToS 97 | 0 push 0 to stack. 1 pushes 1, 2 pushes 2, etc. 98 | A push 10 to stack. B pushes 11, etc. up to F. 99 | " swap top two stack values 100 | l push 1 if ToS-1 < ToS, otherwise 0, discard values used 101 | g push 1 if ToS-1 > ToS, otherwise 0, discard values used 102 | | negate ToS (positive -> negative, negative -> positive) 103 | u un-sign ToS (negative -> positive) 104 | m divide ToS-1 by ToS, pop both, push remainder 105 | 106 | ## I/O commands 107 | 108 | $ input ToS as a number 109 | ~ input ToS as a character 110 | % print ToS as integer 111 | @ print ToS as character 112 | # print ToS as character and pop 113 | 114 | ## Memory commands 115 | 116 | P pop ToS stack to memory 117 | S stash ToS in memory 118 | U pull memory to stack 119 | 120 | ## Conditionals (stolen directly from Surface) 121 | 122 | ? skip one instruction in the direction the IP is facing if ToS is <= 0 123 | ! skip one instruction in the direction the IP is facing if ToS is > 0 124 | s skip the quantity of following instructions as specified by the number contained in ToS 125 | ( set a point to be jumped to by ) and ] 126 | ) unconditionally move the instruction pointer back to the most recent ( 127 | ] move the instruction pointer back to the most recent ( if the ToS is > 0, otherwise do away with the most recently accumulated ( 128 | x do away with the most recently accumulated ( 129 | ; end program if ToS <= 0 130 | 131 | ## Unused characters 132 | 133 | '[{}abfhyGHIJKLMNOQRTVWXYZ 134 | -------------------------------------------------------------------------------- /triangular.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef _WIN32 4 | # include 5 | #endif 6 | 7 | int stack[30000]; 8 | int size; 9 | int memory; 10 | 11 | struct { int x, y, d; } jump[300]; 12 | int jumps; 13 | 14 | extern int display_code; 15 | 16 | extern char buf[1000][1000]; 17 | int direction, skip, x, y, dx, dy; 18 | 19 | enum { UNDEF, NORTHWEST, NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST }; 20 | 21 | void parse(char); 22 | int swap(int*,int*); 23 | 24 | void triangular(void) 25 | { 26 | direction = SOUTHEAST; 27 | 28 | while (1) 29 | { 30 | if (!buf[y][0]) 31 | return; 32 | 33 | parse(buf[y][x]); 34 | 35 | switch (direction) { 36 | case NORTHWEST: dx = -1; dy = -1; break; 37 | case NORTH: dx = -1; dy = -2; break; 38 | case NORTHEAST: dx = 0; dy = -1; break; 39 | case WEST: dx = -1; dy = 0; break; 40 | case EAST: dx = 1; dy = 0; break; 41 | case SOUTHWEST: dx = 0; dy = 1; break; 42 | case SOUTH: dx = 1; dy = 2; break; 43 | case SOUTHEAST: dx = 1; dy = 1; break; 44 | } 45 | 46 | skip = skip < 0 ? 0 : skip; 47 | x += dx * (skip + 1); 48 | y += dy * (skip + 1); 49 | skip = 0; 50 | 51 | if (x < 0 || x > y || y < 0 || y >= 1000 || buf[y][x] <= 0) 52 | exit(EXIT_SUCCESS); 53 | } 54 | } 55 | 56 | int next_direction(int direction, int change) 57 | { 58 | if (change == 1) { 59 | switch (direction) { 60 | case NORTH: return NORTHEAST; 61 | case NORTHEAST: return EAST; 62 | case EAST: return SOUTHEAST; 63 | case SOUTHEAST: return SOUTH; 64 | case SOUTH: return SOUTHWEST; 65 | case SOUTHWEST: return WEST; 66 | case WEST: return NORTHWEST; 67 | case NORTHWEST: return NORTH; 68 | } 69 | } else { 70 | switch (direction) { 71 | case NORTH: return NORTHWEST; 72 | case NORTHEAST: return NORTH; 73 | case EAST: return NORTHEAST; 74 | case SOUTHEAST: return EAST; 75 | case SOUTH: return SOUTHEAST; 76 | case SOUTHWEST: return SOUTH; 77 | case WEST: return SOUTHWEST; 78 | case NORTHWEST: return WEST; 79 | } 80 | } 81 | 82 | return -1; 83 | } 84 | 85 | void parse(char command) 86 | { 87 | if (display_code) { 88 | printf("Executing %c\n",command); 89 | usleep(500000); 90 | } 91 | 92 | switch (command) { 93 | /* directional */ 94 | case '`': direction = NORTHWEST; break; 95 | case '^': direction = NORTH; break; 96 | case '/': direction = NORTHEAST; break; 97 | case '<': direction = WEST; break; 98 | case '>': direction = EAST; break; 99 | case ',': direction = SOUTHWEST; break; 100 | case 'v': direction = SOUTH; break; 101 | case '\\':direction = SOUTHEAST; break; 102 | case 'o': direction = next_direction(direction, 1); break; 103 | case 'e': direction = next_direction(direction, -1); break; 104 | case 'c': direction = next_direction(direction, 1); buf[y][x] = 'z'; break; 105 | case 'z': direction = next_direction(direction, -1); buf[y][x] = 'c'; break; 106 | 107 | /* more directionals jkmnqrtw */ 108 | case 'j': size && stack[size-1] > 0 && (direction = NORTHWEST); break; 109 | case 'k': size && stack[size-1] > 0 && (direction = NORTH); break; 110 | case 'y': size && stack[size-1] > 0 && (direction = NORTHEAST); break; 111 | case 'n': size && stack[size-1] > 0 && (direction = EAST); break; 112 | case 'q': size && stack[size-1] > 0 && (direction = SOUTHEAST); break; 113 | case 'r': size && stack[size-1] > 0 && (direction = SOUTH); break; 114 | case 't': size && stack[size-1] > 0 && (direction = SOUTHWEST); break; 115 | case 'w': size && stack[size-1] > 0 && (direction = WEST); break; 116 | 117 | /* program */ 118 | case '&': exit(EXIT_SUCCESS); 119 | 120 | /* stack */ 121 | case 'p': size > 0 && (size--); break; 122 | case ':': size > 0 && (stack[size] = stack[size-1]); size > 0 && (size++); break; 123 | case '+': size > 1 && (stack[size-2] += stack[size-1]); size > 1 && (size--); break; 124 | case '-': size > 1 && (stack[size-2] -= stack[size-1]); size > 1 && (size--); break; 125 | case '*': size > 1 && (stack[size-2] *= stack[size-1]); size > 1 && (size--); break; 126 | case '_': size > 1 && (stack[size-2] /= stack[size-1]); size > 1 && (size--); break; 127 | case 'i': size > 0 ? (stack[size-1]++) : (stack[size++] = 1); break; 128 | case 'd': size > 0 ? (stack[size-1]--) : (stack[size++] = -1); break; 129 | case '|': size > 0 && (stack[size-1] = -stack[size-1]); break; 130 | case 'm': size > 1 && (stack[size-2] %= stack[size-1]); size > 1 && (size--); break; 131 | case '"': size > 1 && swap(&stack[size-2],&stack[size-1]); break; 132 | 133 | case '$': scanf("%d",&stack[size++]); break; 134 | case '~': stack[size++] = getchar(); break; 135 | case '%': printf("%d",size ? stack[size-1] : 0); break; 136 | case '@': size && putchar(stack[size-1]); break; 137 | case '#': size && putchar(stack[size-1]) && (size--); break; 138 | 139 | case '=': stack[size] = size > 1 ? (stack[size-2] == stack[size-1]) : 0; 140 | size++; 141 | break; 142 | case 'l': size > 1 && (stack[size-2] = (stack[size-2] < stack[size-1])); size > 1 && (size--); break; 143 | case 'g': size > 1 && (stack[size-2] = (stack[size-2] > stack[size-1])); size > 1 && (size--); break; 144 | case 'u': size && stack[size-1] < 0 && (stack[size-1] = -stack[size-1]); 145 | break; 146 | 147 | case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case'8': case '9': 148 | stack[size++] = command - '0'; break; 149 | case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 150 | stack[size++] = command - 'A' + 10; break; 151 | 152 | /* memory */ 153 | case 'P': size && (memory = stack[size-1]); size && (size--); break; 154 | case 'S': size && (memory = stack[size-1]); break; 155 | case 'U': memory && (stack[size++] = memory); break; 156 | 157 | /* conditionals */ 158 | case '?': skip = size ? (stack[size-1] <= 0) : 1; break; 159 | case '!': skip = size ? (stack[size-1] > 0) : 0; break; 160 | case 's': skip = (size ? stack[size-1] : 0); break; 161 | case ';': if (!size || (stack[size-1] <= 0)) exit(EXIT_SUCCESS); break; 162 | 163 | case 'x': jumps && (jumps--); break; 164 | case '(': 165 | jump[jumps].x = x; 166 | jump[jumps].y = y; 167 | jump[jumps].d = direction; 168 | jumps++; 169 | break; 170 | case ')': 171 | if (jumps) { 172 | x = jump[jumps-1].x; 173 | y = jump[jumps-1].y; 174 | direction = jump[jumps-1].d; 175 | } 176 | break; 177 | case ']': 178 | if (jumps && size && stack[size-1]) { 179 | x = jump[jumps-1].x; 180 | y = jump[jumps-1].y; 181 | direction = jump[jumps-1].d; 182 | } else { 183 | jumps && (jumps--); 184 | } 185 | break; 186 | } 187 | } 188 | 189 | int swap(int *a, int *b) 190 | { 191 | int temp = *a; 192 | *a = *b; 193 | *b = temp; 194 | } 195 | --------------------------------------------------------------------------------