Added `gcd` function to stdlib.
[vvhitespace] / vv_compiler.c
CommitLineData
249fb9ba
AT
1/*
2 * (c) 2019 Aaron Taylor <ataylor at subgeniuskitty dot com>
3 * All rights reserved.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <unistd.h>
9#include <string.h>
10#include <errno.h>
11#include <getopt.h>
12#include <stdint.h>
13
14#define VERSION 1
15
16void
17print_usage(char ** argv)
18{
19 printf( "VVhitespace Interpreter v%d (www.subgeniuskitty.com)\n"
20 "Usage: %s -i <file> -o <file>\n"
21 " -h Help (prints this message)\n"
22 " -i <file> Specify a pseudo-VVhitespace source file as input.\n"
23 " -o <file> Specify location for VVhitespace output.\n"
24 , VERSION, argv[0]
25 );
26}
27
f10da6e3 28/* Builds an ASCII string on the stack using VVS PUSH_IMMEDIATE commands. */
971552ff
AT
29/* The syntax: A"test" results in five PUSH_IMMEDIATE commands for the four */
30/* letters, and null-terminator. */
b8b65c17
AT
31/* Expects 'input' to present a double-quoted ('"') ASCII string. */
32/* The 'A' has already been chomped. */
33void
34parse_ascii_string(FILE * input, FILE * output)
35{
36 uint8_t temp_byte;
37 fread(&temp_byte, 1, 1, input);
0e014963
AT
38 if (temp_byte != '"') {
39 /* Die here since we walk the string backward and look for '"' as a terminator. */
40 fprintf(stderr, "ERROR: Expected double-quote to begin string.\n");
41 exit(EXIT_FAILURE);
42 }
b8b65c17
AT
43
44 /* Put the letters on the stack in reverse. Don't forget to put a null-terminator first. */
45 for (fread(&temp_byte,1,1,input); temp_byte != '"'; fread(&temp_byte,1,1,input)) continue;
46 temp_byte = '\0';
47
48 while (temp_byte != '"') {
49 /* First, push three spaces to start a PUSH_IMMEDIATE command for a positive number. */
50 uint8_t temp_output = ' ';
51 for(int i=0;i<3;i++) fwrite(&temp_output, 1, 1, output);
0e014963 52 /* Second, push the ASCII character, 7 bits total, most significant bit first. */
b8b65c17
AT
53 /* Remember, [Tab]=1 and [Space]=0. */
54 uint8_t index = 7; /* 7 bits needed per ASCII character. */
55 while (index) {
56 ((temp_byte >> (index-1)) & 1) ? (temp_output = '\t') : (temp_output = ' ');
57 fwrite(&temp_output, 1, 1, output);
58 index--;
59 }
60 /* Third, close the number with a newline. */
61 temp_output = '\n';
62 fwrite(&temp_output, 1, 1, output);
63
64 /* Read the next byte to prepare for the loop test. */
65 fseek(input, -2, SEEK_CUR);
66 fread(&temp_byte, 1, 1, input);
67 }
971552ff
AT
68
69 /* Advance the stream pointer to the end of the string before returning. */
70 for (fread(&temp_byte,1,1,input); temp_byte != '"'; fread(&temp_byte,1,1,input)) continue;
b8b65c17
AT
71}
72
249fb9ba
AT
73int
74main(int argc, char ** argv)
75{
76 /*
77 * Process command line arguments
78 */
79 int c;
80 FILE * input = NULL, * output = NULL;
81 while ((c = getopt(argc,argv,"i:o:h")) != -1) {
82 switch (c) {
83 case 'i':
84 if ((input = fopen(optarg, "r")) == NULL) {
85 fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
86 }
87 break;
88 case 'o':
89 if ((output = fopen(optarg, "w+")) == NULL) {
90 fprintf(stderr, "ERROR: %s: %s\n", optarg, strerror(errno));
91 }
92 break;
93 case 'h':
94 print_usage(argv);
95 exit(EXIT_SUCCESS);
96 break;
97 default:
98 break;
99 }
100 }
101 if (input == NULL) {
102 fprintf(stderr, "ERROR: Must specify a pseudo-VVhitespace source file with -i flag.\n");
103 print_usage(argv);
104 exit(EXIT_FAILURE);
105 }
106 if (output == NULL) {
107 fprintf(stderr, "ERROR: Must specify destination for VVhitespace source file with -o flag.\n");
108 print_usage(argv);
109 exit(EXIT_FAILURE);
110 }
111
112 /*
113 * Main Loop
114 */
115 uint8_t temp_byte;
116 while (fread(&temp_byte, 1, 1, input)) {
117 switch (temp_byte) {
118 case 't':
119 case 'T':
120 temp_byte = '\t';
121 fwrite(&temp_byte, 1, 1, output);
122 break;
123 case 's':
124 case 'S':
125 temp_byte = ' ';
126 fwrite(&temp_byte, 1, 1, output);
127 break;
128 case 'n':
129 case 'N':
130 temp_byte = '\n';
131 fwrite(&temp_byte, 1, 1, output);
132 break;
133 case 'v':
134 case 'V':
135 temp_byte = '\v';
136 fwrite(&temp_byte, 1, 1, output);
137 break;
b8b65c17
AT
138 case 'a':
139 case 'A':
140 parse_ascii_string(input, output);
141 break;
249fb9ba
AT
142 case '\n':
143 /* Intentionally empty */
144 break;
145 default:
b8b65c17 146 /* The first non-[tTsSnNvVaA] character begins a comment lasting until end of line. */
249fb9ba
AT
147 while (fread(&temp_byte, 1, 1, input)) {
148 if (temp_byte == '\n') break;
149 }
150 break;
151 }
152 }
153
154 /*
155 * Cleanup and exit
156 */
157 fclose(input);
158 fclose(output);
159
249fb9ba
AT
160 exit(EXIT_SUCCESS);
161}