├── .prettierrc ├── src ├── scope.ih ├── scanner.ih ├── bfgenerator.ih ├── parser_types.h ├── makefile.1 ├── makefile.2 ├── instruction.h ├── scanner.cc ├── scope.h ├── compiler.ih ├── typesystem.h ├── scanner.h ├── interpreter │ ├── bfint.h │ ├── main.cc │ └── bfint.cc ├── bfxfunction.h ├── scope.cc ├── todo.txt ├── typesystem.cc ├── memory.h ├── bfgenerator.h ├── lexer ├── main.cc ├── memory.cc ├── scannerbase.h ├── compiler.h ├── bfgenerator.cc └── compilerbase.h ├── std ├── stdbool.bfx ├── std.bfx ├── stdstring.bfx ├── stdmath.bfx └── stdio.bfx ├── bfx_examples ├── gol │ ├── golglider5x5.txt │ ├── golglider10x10.txt │ └── golpenta11x18.txt ├── hello.bfx ├── fib.bfx ├── sieve_bignum.bfx ├── sieve.bfx ├── bfint.bfx ├── bfint_switch.bfx ├── rps.bfx ├── tictactoe.bfx ├── tictactoe_cpu.bfx ├── gol.bfx └── snake.bfx ├── .gitignore ├── Makefile └── LICENCE /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4 3 | } 4 | -------------------------------------------------------------------------------- /src/scope.ih: -------------------------------------------------------------------------------- 1 | #include "scope.h" 2 | #include -------------------------------------------------------------------------------- /std/stdbool.bfx: -------------------------------------------------------------------------------- 1 | const true = 1; 2 | const false = 0; 3 | 4 | -------------------------------------------------------------------------------- /bfx_examples/gol/golglider5x5.txt: -------------------------------------------------------------------------------- 1 | 00000 2 | 00100 3 | 00010 4 | 01110 5 | 00000 6 | -------------------------------------------------------------------------------- /bfx_examples/hello.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | function main() 4 | { 5 | println("Hello, World!"); 6 | } 7 | -------------------------------------------------------------------------------- /std/std.bfx: -------------------------------------------------------------------------------- 1 | include "stdio.bfx" 2 | include "stdmath.bfx" 3 | include "stdbool.bfx" 4 | include "stdstring.bfx" 5 | 6 | -------------------------------------------------------------------------------- /src/scanner.ih: -------------------------------------------------------------------------------- 1 | // Generated by Flexc++ V2.07.07 on Mon, 10 Jan 2022 18:17:24 +0100 2 | 3 | // $insert class_h 4 | #include "scanner.h" 5 | 6 | -------------------------------------------------------------------------------- /src/bfgenerator.ih: -------------------------------------------------------------------------------- 1 | #include "bfgenerator.h" 2 | #include 3 | #include 4 | 5 | #define validateAddr(...) validateAddr__(__func__, __VA_ARGS__) 6 | -------------------------------------------------------------------------------- /src/parser_types.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_TYPES_H 2 | #define PARSER_TYPES_H 3 | 4 | #include "instruction.h" 5 | #include "bfxfunction.h" 6 | #include "typesystem.h" 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /bfx_examples/gol/golglider10x10.txt: -------------------------------------------------------------------------------- 1 | 0000000000 2 | 0100000000 3 | 0010000000 4 | 1110000000 5 | 0000000000 6 | 0000000000 7 | 0000000000 8 | 0000000000 9 | 0000000000 10 | 0000000000 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS system file 2 | .DS_Store 3 | 4 | # any compiled files 5 | *.bf 6 | 7 | # testing files 8 | *.test 9 | .bfxtest* 10 | 11 | # generated files and executables 12 | src/*.o 13 | src/interpreter/*.o 14 | 15 | bfint 16 | bfx 17 | 18 | -------------------------------------------------------------------------------- /bfx_examples/gol/golpenta11x18.txt: -------------------------------------------------------------------------------- 1 | 00000000000 2 | 00000000000 3 | 00000000000 4 | 00000000000 5 | 00000100000 6 | 00000100000 7 | 00001010000 8 | 00000100000 9 | 00000100000 10 | 00000100000 11 | 00000100000 12 | 00001010000 13 | 00000100000 14 | 00000100000 15 | 00000000000 16 | 00000000000 17 | 00000000000 18 | 00000000000 19 | -------------------------------------------------------------------------------- /src/makefile.1: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | CFLAGS=-c -O3 -Wall --std=c++2a -fmax-errors=2 #-Wfatal-errors 3 | GENERATED_FILES=compiler_bisoncpp_generated.cc lex_flexcpp_generated.cc 4 | MY_FILES=main.cc scanner.cc compiler.cc memory.cc bfgenerator.cc typesystem.cc scope.cc 5 | SOURCES=$(GENERATED_FILES) $(MY_FILES) 6 | 7 | OBJECTS=$(SOURCES:.cc=.o) 8 | EXECUTABLE=bfx 9 | 10 | all: $(SOURCES) $(EXECUTABLE) 11 | 12 | $(EXECUTABLE): $(OBJECTS) 13 | $(CC) $(OBJECTS) -o ../$@ 14 | 15 | .cc.o: 16 | $(CC) $(CFLAGS) $< -o $@ -DBFX_DEFAULT_INCLUDE_PATH=$(BFX_DEFAULT_INCLUDE_PATH) 17 | 18 | -------------------------------------------------------------------------------- /src/makefile.2: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | CFLAGS= -c -O3 -Wall --std=c++2a -fmax-errors=2 #-Wfatal-errors 3 | SOURCES=interpreter/bfint.cc interpreter/main.cc 4 | 5 | OBJECTS=$(SOURCES:.cc=.o) 6 | EXECUTABLE=bfint 7 | 8 | all: $(SOURCES) $(EXECUTABLE) 9 | 10 | 11 | ifeq ($(GAMING_MODE_AVAILABLE),1) 12 | $(EXECUTABLE):$(OBJECTS) 13 | $(CC) $(OBJECTS) -o ../$@ -lncurses 14 | 15 | .cc.o: 16 | $(CC) $(CFLAGS) -DUSE_CURSES $< -o $@ 17 | 18 | else 19 | 20 | $(EXECUTABLE):$(OBJECTS) 21 | $(CC) $(OBJECTS) -o ../$@ 22 | 23 | .cc.o: 24 | $(CC) $(CFLAGS) $< -o $@ 25 | 26 | endif 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /bfx_examples/fib.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | function main() 4 | { 5 | prints("f[0] = "); 6 | let a = scand(); 7 | 8 | prints("f[1] = "); 9 | let b = scand(); 10 | 11 | prints("Up to n = "); 12 | let n = scand(); 13 | 14 | prints("0: "); printd(a); endl(); 15 | for (let i = 0; i != n - 1; ++i) 16 | { 17 | printd(i + 1); prints(": "); 18 | printd(b); endl(); 19 | 20 | let tmp = b; 21 | b += a; 22 | a = tmp; 23 | } 24 | 25 | printd(n); prints(": "); 26 | printd(b); endl(); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/instruction.h: -------------------------------------------------------------------------------- 1 | #ifndef INSTRUCTION_H 2 | #define INSTRUCTION_H 3 | 4 | #include 5 | 6 | using Instruction = std::function; 7 | 8 | class AddressOrInstruction 9 | { 10 | enum class Kind 11 | { 12 | ADDRESS, 13 | INSTRUCTION 14 | }; 15 | 16 | mutable Kind d_kind; 17 | mutable int d_addr; 18 | Instruction const d_instr; 19 | 20 | public: 21 | AddressOrInstruction(int addr): 22 | d_kind(Kind::ADDRESS), 23 | d_addr(addr) 24 | {} 25 | 26 | AddressOrInstruction(Instruction const &instr): 27 | d_kind(Kind::INSTRUCTION), 28 | d_instr(instr) 29 | {} 30 | 31 | operator int() const 32 | { 33 | if (d_kind == Kind::INSTRUCTION) 34 | { 35 | // Lazy evaluation 36 | d_addr = d_instr(); 37 | d_kind = Kind::ADDRESS; 38 | } 39 | 40 | return d_addr; 41 | } 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Set default BFX include path and install path 2 | INSTALL_PATH=/usr/local/bin 3 | BFX_INCLUDE=/usr/local/include/bfx 4 | 5 | # Compile with gaming mode available? Requires ncurses 6 | GAMING_MODE_AVAILABLE=1 7 | 8 | .PHONY: bfx bfint 9 | 10 | all: bfx bfint 11 | bfx: 12 | make -C src -f makefile.1 BFX_DEFAULT_INCLUDE_PATH=$(BFX_INCLUDE) 13 | bfint: 14 | make -C src -f makefile.2 GAMING_MODE_AVAILABLE=$(GAMING_MODE_AVAILABLE) 15 | 16 | clean: 17 | rm -f src/*.o src/interpreter/*.o 18 | 19 | regenerate: 20 | cd src && bisonc++ grammar && flexc++ lexer 21 | 22 | install: bfx bfint 23 | cp bfx $(INSTALL_PATH) 24 | cp bfint $(INSTALL_PATH) 25 | mkdir -p $(BFX_INCLUDE) 26 | cp std/std.bfx $(BFX_INCLUDE) 27 | cp std/stdio.bfx $(BFX_INCLUDE) 28 | cp std/stdmath.bfx $(BFX_INCLUDE) 29 | cp std/stdbool.bfx $(BFX_INCLUDE) 30 | cp std/stdstring.bfx $(BFX_INCLUDE) 31 | 32 | uninstall: 33 | rm -f $(INSTALL_PATH)/bfx 34 | rm -f $(INSTALL_PATH)/bfint 35 | rm -rf $(BFX_INCLUDE) 36 | -------------------------------------------------------------------------------- /bfx_examples/sieve_bignum.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | const ARRAY_SIZE = 1000; 4 | 5 | function main() 6 | { 7 | println("Welcome to the BrainF*ck prime-sieve!"); 8 | prints("Enter n (0-1000): "); 9 | let n = scand(); 10 | 11 | // Initialize array and calculate maximum sieve-value 12 | let [ARRAY_SIZE] arr = 1; 13 | let s = sqrt(n); 14 | 15 | // Sieve 16 | let prime = 2; 17 | while (prime <= s) 18 | { 19 | for (let i = square(prime); i < n; i += prime) 20 | arr[i] = 0; 21 | 22 | ++prime; 23 | while (arr[prime] == 0) 24 | ++prime; 25 | } 26 | 27 | // Print results 28 | printPrimes(arr, n); 29 | } 30 | 31 | function printPrimes(&arr, n) 32 | { 33 | let count = 0; 34 | for (let i = 2; i < n; ++i) 35 | { 36 | if (arr[i]) 37 | { 38 | printd(i); endl(); 39 | ++count; 40 | } 41 | } 42 | 43 | prints("Found "); printd(count); println(" primes!"); 44 | } 45 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Joren Heit 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/scanner.cc: -------------------------------------------------------------------------------- 1 | #include "scanner.ih" 2 | 3 | 4 | Scanner::Scanner(std::string const &infile, std::string const &outfile): 5 | ScannerBase(infile, outfile) 6 | { 7 | // d_startConditionStack.push(StartCondition_::INITIAL); 8 | } 9 | 10 | 11 | char Scanner::escape(char c) 12 | { 13 | switch (c) 14 | { 15 | case 'n': return '\n'; 16 | case 't': return '\t'; 17 | case '0': return '\0'; 18 | default: return c; 19 | } 20 | } 21 | 22 | char Scanner::escapeTestContent(std::string const &str) 23 | { 24 | try{ 25 | return static_cast(std::stoi(str)); 26 | } catch(std::invalid_argument const &) 27 | { 28 | std::cerr << "ERROR: escape sequence does not contain a number.\n"; 29 | std::exit(1); 30 | } 31 | } 32 | 33 | void Scanner::pushStartCondition(StartCondition_ next) 34 | { 35 | d_startConditionStack.push(startCondition()); 36 | begin(next); 37 | } 38 | 39 | void Scanner::popStartCondition() 40 | { 41 | begin(d_startConditionStack.top()); 42 | d_startConditionStack.pop(); 43 | } 44 | -------------------------------------------------------------------------------- /src/scope.h: -------------------------------------------------------------------------------- 1 | #ifndef SCOPE_H 2 | #define SCOPE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class Scope 9 | { 10 | public: 11 | enum class Type 12 | { 13 | Function, 14 | For, 15 | While, 16 | Switch, 17 | If, 18 | Anonymous 19 | }; 20 | 21 | private: 22 | template 23 | using StackType = std::deque; 24 | 25 | struct SubScope 26 | { 27 | Type type; 28 | int id; 29 | }; 30 | 31 | StackType>> d_stack; 32 | 33 | public: 34 | bool empty() const; 35 | std::string function() const; 36 | std::string current() const; 37 | Type currentType() const; 38 | std::string enclosing() const; 39 | bool containsFunction(std::string const &name) const; 40 | void push(Type type); 41 | void push(std::string const &name); 42 | std::string popFunction(std::string const &name); 43 | std::pair pop(); 44 | }; 45 | 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /std/stdstring.bfx: -------------------------------------------------------------------------------- 1 | // STD STRINGS 2 | 3 | /* Provided string functions: 4 | 5 | strcpy(str, dest) - Copies entire string's contents to array (without checking for buffer overflow) 6 | Returns the number of copied elements. 7 | strlen(str) - Returns the length of the string, up to terminating 0. 8 | strcmp(str1, str2) - Compares the two strings. Returns 0 when unequal, 1 when equal. 9 | */ 10 | 11 | function n = strcpy(&dest, &str) 12 | { 13 | let n = 0; 14 | while (n < sizeof(str) && str[n] != 0) 15 | { 16 | dest[n] = str[n]; 17 | ++n; 18 | } 19 | } 20 | 21 | function n = strlen(&str) 22 | { 23 | let n = 0; 24 | while (n < sizeof(str) && str[n] != 0) 25 | ++n; 26 | } 27 | 28 | function x = strcmp(&str1, &str2) 29 | { 30 | let n1 = strlen(str1); 31 | let n2 = strlen(str2); 32 | 33 | let x = 1; 34 | if (n1 != n2) 35 | { 36 | x = 0; 37 | } 38 | else 39 | { 40 | for (let i = 0; i != n1; ++i) 41 | if (str1[i] != str2[i]) 42 | x = 0; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/compiler.ih: -------------------------------------------------------------------------------- 1 | // Generated by Bisonc++ V6.03.00 on Sun, 06 Feb 2022 11:15:45 +0100 2 | 3 | // Include this file in the sources of the class Compiler. 4 | 5 | // $insert class.h 6 | #include 7 | #include 8 | #include 9 | #include "compiler.h" 10 | 11 | inline void Compiler::print() 12 | { 13 | 14 | } 15 | 16 | inline void Compiler::exceptionHandler(std::exception const &exc) 17 | { 18 | throw; // re-implement to handle exceptions thrown by actions 19 | } 20 | 21 | 22 | // Add here includes that are only required for the compilation 23 | // of Compiler's sources. 24 | 25 | 26 | 27 | // UN-comment the next using-declaration if you want to use 28 | // int Compiler's sources symbols from the namespace std without 29 | // specifying std:: 30 | 31 | //using namespace std; 32 | 33 | #ifndef BFX_DEFAULT_INCLUDE_PATH 34 | #error "Macro BFX_DEFAULT_INCLUDE_PATH undefined." 35 | #endif 36 | 37 | #define STRINGIFY(x) STRINGIFY2(x) 38 | #define STRINGIFY2(x) #x 39 | #define BFX_DEFAULT_INCLUDE_PATH_STRING STRINGIFY(BFX_DEFAULT_INCLUDE_PATH) 40 | -------------------------------------------------------------------------------- /bfx_examples/sieve.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | const MAX_INT_SIZE = 255; 4 | const ARRAY_SIZE = 250; 5 | 6 | function incr(&x, &p, &n) 7 | { 8 | if (MAX_INT_SIZE - x < p) // would cause overflow 9 | x = n; 10 | else 11 | x += p; // safe to increment 12 | } 13 | 14 | function main() 15 | { 16 | println("Welcome to the BrainF*ck prime-sieve!"); 17 | prints("Enter n (0-250): "); 18 | let n = scand(); 19 | 20 | // Initialize array and calculate maximum sieve-value 21 | let [ARRAY_SIZE] arr = 1; 22 | let s = sqrt(n); 23 | 24 | // Sieve 25 | let prime = 2; 26 | while (prime <= s) 27 | { 28 | for (let i = prime^2; i < n; incr(i, prime, n)) // protect against overflow 29 | arr[i] = 0; 30 | 31 | ++prime; 32 | while (arr[prime] == 0) 33 | ++prime; 34 | } 35 | 36 | // Print results 37 | printPrimes(arr, n); 38 | } 39 | 40 | function printPrimes(&arr, n) 41 | { 42 | let count = 0; 43 | for (let i = 2; i < n; ++i) 44 | { 45 | if (arr[i]) 46 | { 47 | printd(i); endl(); 48 | ++count; 49 | } 50 | } 51 | 52 | prints("Found "); printd(count); println(" primes!"); 53 | } 54 | -------------------------------------------------------------------------------- /src/typesystem.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPESYSTEM_H 2 | #define TYPESYSTEM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace TypeSystem 10 | { 11 | struct Field; 12 | 13 | class Type 14 | { 15 | enum class Kind 16 | { 17 | NULLTYPE, 18 | INT, 19 | STRUCT 20 | }; 21 | 22 | int d_size{-1}; 23 | std::string d_name{""}; 24 | Kind d_kind{Kind::NULLTYPE}; 25 | 26 | public: 27 | Type() = default; 28 | 29 | Type(std::string const &name): 30 | d_name(name), 31 | d_kind(Kind::STRUCT) 32 | {} 33 | 34 | Type(int const sz): 35 | d_size(sz), 36 | d_kind(Kind::INT) 37 | {} 38 | 39 | int size() const; 40 | std::string name() const; 41 | bool defined() const; 42 | std::vector fields() const; 43 | bool operator==(Type const &other) const; 44 | bool isIntType() const; 45 | bool isStructType() const; 46 | bool isNullType() const; 47 | }; 48 | 49 | struct Field 50 | { 51 | std::string name; 52 | int offset; 53 | Type type; 54 | }; 55 | 56 | 57 | bool add(std::string const &name, 58 | std::vector> const &fields); 59 | }; 60 | 61 | #endif // TYPES_H 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/scanner.h: -------------------------------------------------------------------------------- 1 | // Generated by Flexc++ V2.07.07 on Mon, 10 Jan 2022 18:17:24 +0100 2 | 3 | #ifndef Scanner_H_INCLUDED_ 4 | #define Scanner_H_INCLUDED_ 5 | 6 | #include 7 | 8 | // $insert baseclass_h 9 | #include "scannerbase.h" 10 | #include "compilerbase.h" 11 | 12 | // $insert classHead 13 | class Scanner: public ScannerBase 14 | { 15 | int d_nestedCommentLevel{0}; 16 | std::stack d_startConditionStack; 17 | 18 | public: 19 | Scanner(std::string const &infile, std::string const &outfile); 20 | using ScannerBase::pushStream; 21 | 22 | // $insert lexFunctionDecl 23 | int lex(); 24 | 25 | private: 26 | int lex_(); 27 | int executeAction_(size_t ruleNr); 28 | 29 | void print(); 30 | void preCode(); // re-implement this function for code that must 31 | // be exec'ed before the patternmatching starts 32 | 33 | void postCode(PostEnum_ type); 34 | // re-implement this function for code that must 35 | // be exec'ed after the rules's actions. 36 | 37 | void pushStartCondition(StartCondition_ next); 38 | void popStartCondition(); 39 | 40 | static char escape(char c); 41 | static char escapeTestContent(std::string const &matched); 42 | }; 43 | 44 | // $insert inlineLexFunction 45 | inline int Scanner::lex() 46 | { 47 | return lex_(); 48 | } 49 | 50 | inline void Scanner::preCode() 51 | { 52 | // optionally replace by your own code 53 | } 54 | 55 | inline void Scanner::postCode([[maybe_unused]] PostEnum_ type) 56 | { 57 | // optionally replace by your own code 58 | } 59 | 60 | inline void Scanner::print() 61 | { 62 | print_(); 63 | } 64 | 65 | #endif // Scanner_H_INCLUDED_ 66 | 67 | -------------------------------------------------------------------------------- /bfx_examples/bfint.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | const MAX_SIZE = 250; 4 | 5 | struct BF 6 | { 7 | [MAX_SIZE] tape; 8 | [MAX_SIZE] code; 9 | [MAX_SIZE] jmp_stack; 10 | 11 | tape_pointer, stack_pointer; 12 | }; 13 | 14 | global [struct BF] bf; 15 | 16 | function main() 17 | { 18 | let programSize = load(); 19 | run(programSize); 20 | } 21 | 22 | function init() 23 | { 24 | bf.tape = 0; 25 | bf.tape_pointer = 0; 26 | } 27 | 28 | function count = load() 29 | { 30 | let count = 0; 31 | let c; 32 | while ((c = scanc()) != '\n' && count < MAX_SIZE) 33 | bf.code[count++] = c; 34 | } 35 | 36 | function run(programSize) 37 | { 38 | let i = 0; 39 | while (i < programSize) 40 | { 41 | let op = bf.code[i]; 42 | if (op == '>') 43 | { 44 | ++bf.tape_pointer; 45 | } 46 | else if (op == '<') 47 | { 48 | --bf.tape_pointer; 49 | } 50 | else if (op == '[') 51 | { 52 | if (bf.tape[bf.tape_pointer]) 53 | bf.jmp_stack[bf.stack_pointer++] = i; 54 | else 55 | skipToClosingBracket(i); 56 | } 57 | else if (op == ']') 58 | { 59 | if (bf.tape[bf.tape_pointer]) 60 | i = bf.jmp_stack[bf.stack_pointer - 1]; 61 | else 62 | --bf.stack_pointer; 63 | } 64 | else if (op == '+') 65 | { 66 | ++bf.tape[bf.tape_pointer]; 67 | } 68 | else if (op == '-') 69 | { 70 | --bf.tape[bf.tape_pointer]; 71 | } 72 | else if (op == '.') 73 | { 74 | printc(bf.tape[bf.tape_pointer]); 75 | } 76 | else if (op == ',') 77 | { 78 | bf.tape[bf.tape_pointer] = scanc(); 79 | } 80 | 81 | ++i; 82 | } 83 | } 84 | 85 | function skipToClosingBracket(&idx) 86 | { 87 | let count = 1; 88 | while (count > 0) 89 | { 90 | let op = bf.code[++idx]; 91 | if (op == '[') 92 | ++count; 93 | else if (op == ']') 94 | --count; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/interpreter/bfint.h: -------------------------------------------------------------------------------- 1 | #ifndef BFINT_H 2 | #define BFINT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum class CellType 10 | { 11 | INT8, 12 | INT16, 13 | INT32 14 | }; 15 | 16 | struct Options 17 | { 18 | int err{0}; 19 | CellType cellType{CellType::INT8}; 20 | int tapeLength{30000}; 21 | std::string bfFile; 22 | std::string testFile; 23 | bool randomEnabled{false}; 24 | int randMax{0}; 25 | bool randomWarningEnabled{true}; 26 | bool gamingMode{false}; 27 | }; 28 | 29 | class BFInterpreter 30 | { 31 | std::vector d_array; 32 | std::string d_code; 33 | size_t d_arrayPointer{0}; 34 | size_t d_codePointer{0}; 35 | std::stack d_loopStack; 36 | 37 | using RngType = std::mt19937; 38 | std::uniform_int_distribution d_uniformDist; 39 | RngType d_rng; 40 | 41 | // Options 42 | CellType const d_cellType; 43 | bool const d_randomEnabled{false}; 44 | int const d_randMax{0}; 45 | bool const d_randomWarningEnabled{true}; 46 | bool const d_gamingMode{false}; 47 | std::string const d_testFile; 48 | 49 | enum Ops: char 50 | { 51 | PLUS = '+', 52 | MINUS = '-', 53 | LEFT = '<', 54 | RIGHT = '>', 55 | START_LOOP = '[', 56 | END_LOOP = ']', 57 | PRINT = '.', 58 | READ = ',', 59 | RAND = '?', 60 | }; 61 | 62 | public: 63 | BFInterpreter(Options const &opt); 64 | int run(); 65 | 66 | private: 67 | int run(std::istream &in, std::ostream &out); 68 | int consume(Ops op); 69 | void plus(); 70 | void minus(); 71 | void pointerInc(); 72 | void pointerDec(); 73 | void startLoop(); 74 | void endLoop(); 75 | void print(std::ostream &); 76 | void printCurses(); 77 | void read(std::istream &); 78 | void readCurses(); 79 | void random(); 80 | void printState(); 81 | void handleAnsi(std::string &ansiStr, bool const force); 82 | static void finish(int sig); 83 | void runTests(); 84 | void reset(); 85 | }; 86 | 87 | 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /bfx_examples/bfint_switch.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | const MAX_SIZE = 250; 4 | 5 | struct BF 6 | { 7 | [MAX_SIZE] tape; 8 | [MAX_SIZE] code; 9 | [MAX_SIZE] jmp_stack; 10 | 11 | tape_pointer, stack_pointer; 12 | }; 13 | 14 | global [struct BF] bf; 15 | 16 | function main() 17 | { 18 | let programSize = load(); 19 | run(programSize); 20 | } 21 | 22 | function init() 23 | { 24 | bf.tape = 0; 25 | bf.tape_pointer = 0; 26 | } 27 | 28 | function count = load() 29 | { 30 | let count = 0; 31 | let c; 32 | while ((c = scanc()) != '\n' && count < MAX_SIZE) 33 | bf.code[count++] = c; 34 | } 35 | 36 | function run(programSize) 37 | { 38 | let i = 0; 39 | while (i < programSize) 40 | { 41 | switch (bf.code[i]) 42 | { 43 | case '>': 44 | { 45 | ++bf.tape_pointer; 46 | } 47 | case '<': 48 | { 49 | --bf.tape_pointer; 50 | } 51 | case '[': 52 | { 53 | if (bf.tape[bf.tape_pointer]) 54 | bf.jmp_stack[bf.stack_pointer++] = i; 55 | else 56 | skipToClosingBracket(i); 57 | } 58 | case ']': 59 | { 60 | if (bf.tape[bf.tape_pointer]) 61 | i = bf.jmp_stack[bf.stack_pointer - 1]; 62 | else 63 | --bf.stack_pointer; 64 | } 65 | case '+': 66 | { 67 | ++bf.tape[bf.tape_pointer]; 68 | } 69 | case '-': 70 | { 71 | --bf.tape[bf.tape_pointer]; 72 | } 73 | case '.': 74 | { 75 | printc(bf.tape[bf.tape_pointer]); 76 | } 77 | case ',': 78 | { 79 | bf.tape[bf.tape_pointer] = scanc(); 80 | } 81 | } 82 | ++i; 83 | } 84 | } 85 | 86 | function skipToClosingBracket(&idx) 87 | { 88 | let count = 1; 89 | while (count > 0) 90 | { 91 | let op = bf.code[++idx]; 92 | if (op == '[') 93 | ++count; 94 | else if (op == ']') 95 | --count; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/bfxfunction.h: -------------------------------------------------------------------------------- 1 | #ifndef BFXFUNCTION_H 2 | #define BFXFUNCTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "instruction.h" 8 | 9 | class BFXFunction 10 | { 11 | public: 12 | enum class ParameterType 13 | { 14 | Value, 15 | Reference 16 | }; 17 | 18 | using Parameter = std::pair; 19 | 20 | private: 21 | std::string d_name; 22 | Instruction d_body; 23 | std::vector d_params; 24 | std::string d_returnVar; 25 | std::string d_mangled; 26 | 27 | public: 28 | BFXFunction() 29 | { 30 | assert(false && "Default constructor shouldn't be called."); 31 | 32 | // Default constructor must be declared for some reason 33 | // to be compatible with the CompilerBase class generated by 34 | // bisonc++, but is never actually called (and shouldn't be). 35 | } 36 | 37 | BFXFunction(std::string name, std::vector const ¶ms): 38 | d_name(name), 39 | d_params(params), 40 | d_mangled(mangle(name, params.size())) 41 | {} 42 | 43 | BFXFunction &setBody(Instruction const &body) 44 | { 45 | d_body = body; 46 | return *this; 47 | } 48 | 49 | BFXFunction &setReturnVariable(std::string const &ident) 50 | { 51 | d_returnVar = ident; 52 | return *this; 53 | } 54 | 55 | Instruction const &body() const 56 | { 57 | return d_body; 58 | } 59 | 60 | std::vector const ¶ms() const 61 | { 62 | return d_params; 63 | } 64 | 65 | std::string const &returnVariable() const 66 | { 67 | return d_returnVar; 68 | } 69 | 70 | std::string const &name() const 71 | { 72 | return d_name; 73 | } 74 | 75 | std::string const &mangled() const 76 | { 77 | return d_mangled; 78 | } 79 | 80 | bool isVoid() const 81 | { 82 | return d_returnVar.empty(); 83 | } 84 | 85 | public: 86 | static std::string mangle(BFXFunction const &func) 87 | { 88 | return mangle(func.name(), func.params().size()); 89 | } 90 | 91 | 92 | static std::string mangle(std::string const &name, int const nArgs) 93 | { 94 | return std::string("__f_") + std::to_string(nArgs) + "_" + name; 95 | } 96 | }; 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/scope.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "scope.ih" 3 | 4 | bool Scope::empty() const 5 | { 6 | return d_stack.empty(); 7 | } 8 | 9 | std::string Scope::function() const 10 | { 11 | return empty() ? "" : d_stack.back().first; 12 | } 13 | 14 | std::string Scope::current() const 15 | { 16 | std::string result = function(); 17 | if (d_stack.size() == 0) 18 | return result; 19 | 20 | for (SubScope const &sub: d_stack.back().second) 21 | { 22 | result += std::string("::") + std::to_string(sub.id); 23 | } 24 | 25 | return result; 26 | } 27 | 28 | Scope::Type Scope::currentType() const 29 | { 30 | if (d_stack.back().second.empty()) 31 | return Type::Function; 32 | else 33 | return d_stack.back().second.back().type; 34 | } 35 | 36 | std::string Scope::enclosing() const 37 | { 38 | std::string const curr = current(); 39 | return d_stack.back().second.empty() ? "" : curr.substr(0, curr.find_last_of("::") - 1); 40 | } 41 | 42 | bool Scope::containsFunction(std::string const &name) const 43 | { 44 | auto const it = std::find_if(d_stack.begin(), d_stack.end(), 45 | [&](auto const &item) 46 | { 47 | return item.first == name; 48 | }); 49 | 50 | return it != d_stack.end(); 51 | } 52 | 53 | std::string Scope::popFunction(std::string const &name) 54 | { 55 | assert(name == function() && "trying to exit function-scope other than current function"); 56 | 57 | std::string top = current(); 58 | d_stack.pop_back(); 59 | return top; 60 | } 61 | 62 | void Scope::push(Type type) 63 | { 64 | static int counter = 0; 65 | d_stack.back().second.push_back({ 66 | .type = type, 67 | .id = ++counter 68 | }); 69 | } 70 | 71 | void Scope::push(std::string const &name) 72 | { 73 | d_stack.push_back({name, {}}); 74 | // d_stack.emplace_back(name, StackType{}); 75 | // TODO: check 76 | } 77 | 78 | std::pair Scope::pop() 79 | { 80 | std::string const previousScopeString = current(); 81 | 82 | auto &subScopeStack = d_stack.back().second; 83 | Type const previousScopeType = subScopeStack.back().type; 84 | subScopeStack.pop_back(); 85 | 86 | return {previousScopeString, previousScopeType}; 87 | } 88 | 89 | -------------------------------------------------------------------------------- /bfx_examples/rps.bfx: -------------------------------------------------------------------------------- 1 | // Note: compile and run with --random, will not compile with --no-bcr 2 | 3 | include "std.bfx" 4 | 5 | global [9] WIN_ARRAY; 6 | 7 | function init() 8 | { 9 | WIN_ARRAY = #{0, 2, 1, 10 | 1, 0, 2, 11 | 2, 1, 0}; 12 | } 13 | 14 | function x = validate(&c) 15 | { 16 | let x = (c == 'R' || c == 'P' || c == 'S'); 17 | } 18 | 19 | function i = toIndex(&c) 20 | { 21 | let i; 22 | switch (c) 23 | { 24 | case 'R': i = 0; 25 | case 'P': i = 1; 26 | case 'S': i = 2; 27 | } 28 | } 29 | 30 | function w = winner(&p1, &p2) 31 | { 32 | let w = WIN_ARRAY[3 * p1 + p2]; 33 | } 34 | 35 | function printScore(&s) 36 | { 37 | prints("Score: "); 38 | printd(s[0]); 39 | prints(" - "); 40 | printd(s[1]); 41 | } 42 | 43 | function main() 44 | { 45 | init(); 46 | let input; 47 | let [2] score = 0; 48 | 49 | while (true) 50 | { 51 | // Get user input 52 | prints("Choose: Rock (R), Paper (P) or Scissors (S): "); 53 | scans(input); 54 | if (!validate(input)) 55 | { 56 | println("Invalid response. Enter R, P or S."); 57 | continue; 58 | } 59 | 60 | // Generate computer move 61 | let computer = rand() % 3; 62 | prints("Computer chose: "); 63 | switch (computer) 64 | { 65 | case 0: println("Rock!"); 66 | case 1: println("Paper!"); 67 | case 2: println("Scissors!"); 68 | } 69 | 70 | // Determine winner 71 | switch (winner(toIndex(input), computer)) 72 | { 73 | case 0: prints("It's a tie! "); 74 | case 1: 75 | { 76 | prints("You win! "); 77 | ++score[0]; 78 | } 79 | case 2: 80 | { 81 | prints("Computer wins! "); 82 | ++score[1]; 83 | } 84 | } 85 | printScore(score); 86 | endl(); 87 | 88 | // Again? 89 | prints("Play again? (y/n) "); 90 | scans(input); 91 | switch (input) 92 | { 93 | case 'n': break; 94 | case 'y': endl(); 95 | default: 96 | { 97 | println("Invalid input; quitting."); 98 | return; 99 | } 100 | } 101 | } 102 | 103 | endl(); 104 | println("Thanks for playing! "); 105 | printScore(score); 106 | endl(); 107 | } 108 | -------------------------------------------------------------------------------- /src/todo.txt: -------------------------------------------------------------------------------- 1 | 2 | ' 3 | [x] consteval should take into account the int-type 4 | [x] Bounds checking for known index --> warning or error? 5 | [x] implement while* 6 | [x] make constant evaluation a compiler option (maybe -O0, -O1?) 7 | [x] rename compilerError to error -> see if error() generated by bisonc++ can have other name 8 | [ ] implement optimizing function that reduces e.g. +++++++.[-]+++++ to +++++++.-- 9 | OR revisit keeping track of runtime values and using these to optimize runtimeSetToValue() 10 | [ ] Constants are still needed for O0 generation. However, it should be possible to have array-sizes 11 | specified by variables known at compile-time. 12 | [x] remove __bf and __movePtr 13 | [x] Update readme 14 | - new array init syntax: #{} 15 | - difference between O0 and O1 16 | - section "Numbers" 17 | - indexing: bounds check 18 | - for* while* 19 | [x] Bug: runtime value 0 as index does not return first element 20 | [x] for-loop init is now in calling scope -> move to for-scope 21 | [x] Check: on disableConstEval, do we need to sync all cells in scope? Or can we skip temps? 22 | --> YES, WE DO 23 | [x] Check: in Memory::freeTemps() can we speed up by incrementing idx after array-free? 24 | [x] Check: stdlib, scand and scand_4 should use to_int 25 | [x] Crash when return value not specified in function header?! 26 | [x] for (let i = 0; i != 255; ++i) doesn't run 27 | [x] Why does the memory start allocating at cell 2? 28 | [x] merge for/while condition with breakflag 29 | [x] --no-bcr 30 | [x] Better include: detect (and ignore) circular inclusion, use PATH, slash at the end. 31 | [x] In int16 mode, possible to have control characters that move cursor to prev line? 32 | --> No int16 mode necessary. Use ANSI escape sequences. Works, but tedious. 33 | [x] Add basic cursor movement to library using ANSI escape sequences 34 | [x] Better solution to pass compiler options 35 | [x] MAX_LOOP_UNROLL_ITERATIONS as compiler option 36 | [ ] anonymous scope block does not create new scope 37 | [x] add note to readme about let [] x = str[0] 38 | [x] add memory profiling option 39 | [x] something weird going on: syntax error on e.g. "x++ > 0", only with post-increment. 40 | [x] let str = "bla" works for some reason 41 | [ ] implement more string functions: strlen, strcmp, memcpy, ...? 42 | [ ] Wild idea: lazy allocation --> only allocate when synced? would that compress the memory footprint in O1 mode? 43 | Would need to keep track of a virtual memory array. Probably very tedious to implement. 44 | [x] Shortcircuit on && and ||? ==> hard! Make note in readme that this is not supported. 45 | [ ] Gaming mode does not always work in with int16 type. 46 | [x] Use Options struct for bfint as well 47 | [ ] Create git wiki to document the BF algorithms/compiler implementation details 48 | [ ] Unit test framework / test suite 49 | [ ] In the lexer, remove more() before miniscanner to simplify substring 50 | -------------------------------------------------------------------------------- /src/typesystem.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "typesystem.h" 3 | 4 | namespace TypeSystem 5 | { 6 | class StructDefinition 7 | { 8 | private: 9 | int d_size; 10 | bool const d_valid{true}; 11 | std::string d_name; 12 | std::vector d_fields; 13 | 14 | public: 15 | StructDefinition(std::string const &name): 16 | d_size(1), 17 | d_name(name) 18 | {} 19 | 20 | StructDefinition(): 21 | d_valid{false} 22 | {} 23 | 24 | void addField(std::string const &name, Type const &type) 25 | { 26 | d_fields.emplace_back(Field{name, d_size, type}); 27 | d_size += type.size(); 28 | } 29 | 30 | int size() const 31 | { 32 | return d_size; 33 | } 34 | 35 | std::vector const &fields() const 36 | { 37 | return d_fields; 38 | } 39 | 40 | std::string const &name() const 41 | { 42 | return d_name; 43 | } 44 | }; 45 | 46 | static std::map typeMap; 47 | 48 | static std::string intName(int const sz) 49 | { 50 | return "__int_" + std::to_string(sz) + "__"; 51 | } 52 | } 53 | 54 | int TypeSystem::Type::size() const 55 | { 56 | if (isIntType()) 57 | return d_size; 58 | 59 | auto const it = TypeSystem::typeMap.find(d_name); 60 | if (it == TypeSystem::typeMap.end()) 61 | return -1; 62 | 63 | return (it->second).size(); 64 | } 65 | 66 | std::string TypeSystem::Type::name() const 67 | { 68 | return isStructType() ? d_name : TypeSystem::intName(d_size); 69 | } 70 | 71 | bool TypeSystem::Type::defined() const 72 | { 73 | if (isIntType() || isNullType()) 74 | return true; 75 | 76 | auto const it = TypeSystem::typeMap.find(name()); 77 | return it != TypeSystem::typeMap.end(); 78 | } 79 | 80 | std::vector TypeSystem::Type::fields() const 81 | { 82 | assert(defined() && isStructType() && "requesting fields of undefined type."); 83 | 84 | return typeMap.at(name()).fields(); 85 | } 86 | 87 | bool TypeSystem::Type::operator==(Type const &other) const 88 | { 89 | return name() == other.name(); 90 | } 91 | 92 | bool TypeSystem::Type::isIntType() const 93 | { 94 | return d_kind == Kind::INT; 95 | } 96 | 97 | bool TypeSystem::Type::isStructType() const 98 | { 99 | return d_kind == Kind::STRUCT; 100 | } 101 | 102 | bool TypeSystem::Type::isNullType() const 103 | { 104 | return d_kind == Kind::NULLTYPE; 105 | } 106 | 107 | bool TypeSystem::add(std::string const &name, 108 | std::vector> const &fields) 109 | { 110 | if (typeMap.find(name) != typeMap.end()) 111 | return false; 112 | 113 | StructDefinition s(name); 114 | for (auto const &f: fields) 115 | { 116 | s.addField(f.first, f.second); 117 | } 118 | 119 | typeMap.insert({name, s}); 120 | return true; 121 | } 122 | -------------------------------------------------------------------------------- /std/stdmath.bfx: -------------------------------------------------------------------------------- 1 | // STD MATH 2 | 3 | /* Provided math functions: 4 | 5 | MATHS 6 | 7 | sqrt(x) - Calculate the square root of x, rounded down 8 | factorial(n) - Calculate n! (overflows for n > 5) 9 | min(x,y) - Returns the minimum of x and y 10 | max(x,y) - Returns the maximum of x and y 11 | 12 | BINARY 13 | 14 | to_binary_array(x) - Returns 8-bit binary representation of x as array of 0's and 1's 15 | from_binary_array(x) - Takes a binary array and converts it back to 8-bit integer 16 | 17 | BITWISE OPERATIONS 18 | 19 | bit8_or(x, y) - Returns bitwise OR of x and y (8-bit) 20 | bit8_and(x, y) - Returns bitwise AND of x and y (8-bit) 21 | bit8_xor(x, y) - Returns bitwise XOR of x and y (8-bit) 22 | bit8_shift_left(x, n) - Shift the binary representation of x by n bits to the left 23 | bit8_shift_right(x, n) - Shift the binary representation of x by n bits to the right 24 | 25 | RANDOM NUMBER GENERATOR 26 | 27 | rand() - Generate random number 28 | */ 29 | 30 | 31 | function s = sqrt(&x) 32 | { 33 | let s = 0; 34 | let previous = 0; 35 | let y = 1; 36 | while (s == 0) 37 | { 38 | let y2 = y^2; 39 | if (y2 == x) 40 | s = y; 41 | else if (y2 > x || y2 < previous) // check for overflow 42 | s = y - 1; 43 | 44 | previous = y2; 45 | ++y; 46 | } 47 | } 48 | 49 | function f = factorial(&x) 50 | { 51 | let f = 1; 52 | for (let i = 1; i <= x; ++i) 53 | f *= i; 54 | } 55 | 56 | function m = min(&x, &y) 57 | { 58 | let m; 59 | if (x <= y) 60 | m = x; 61 | else 62 | m = y; 63 | } 64 | 65 | function m = max(&x, &y) 66 | { 67 | let m; 68 | if (x >= y) 69 | m = x; 70 | else 71 | m = y; 72 | } 73 | 74 | function arr = to_binary_array(x) 75 | { 76 | let [] powers = #{128, 64, 32, 16, 8, 4, 2, 1}; 77 | let [8] arr = 0; 78 | for(let i = 0; i < sizeof(powers); ++i) 79 | { 80 | let p = powers[i]; 81 | if (x >= p) 82 | { 83 | arr[i] = 1; 84 | x -= p; 85 | } 86 | } 87 | } 88 | 89 | function x = from_binary_array(&arr) 90 | { 91 | let [] powers = #{128, 64, 32, 16, 8, 4, 2, 1}; 92 | let x = 0; 93 | for(let i = 0; i < sizeof(powers); ++i) 94 | { 95 | if (arr[i]) 96 | x += powers[i]; 97 | } 98 | } 99 | 100 | function x = bit8_or(x, &y) 101 | { 102 | let [8] xbits = to_binary_array(x); 103 | let [8] ybits = to_binary_array(y); 104 | 105 | for (let i = 0; i != sizeof(xbits); ++i) 106 | { 107 | xbits[i] = xbits[i] || ybits[i]; 108 | } 109 | 110 | x = from_binary_array(xbits); 111 | } 112 | 113 | function x = bit8_and(x, &y) 114 | { 115 | let [8] xbits = to_binary_array(x); 116 | let [8] ybits = to_binary_array(y); 117 | 118 | for (let i = 0; i != sizeof(xbits); ++i) 119 | { 120 | xbits[i] = xbits[i] && ybits[i]; 121 | } 122 | 123 | x = from_binary_array(xbits); 124 | } 125 | 126 | function x = bit8_xor(x, &y) 127 | { 128 | let [8] xbits = to_binary_array(x); 129 | let [8] ybits = to_binary_array(y); 130 | 131 | for (let i = 0; i != sizeof(xbits); ++i) 132 | { 133 | xbits[i] = xbits[i] != ybits[i]; 134 | } 135 | 136 | x = from_binary_array(xbits); 137 | } 138 | 139 | function y = bit8_shift_left(&x, &n) 140 | { 141 | let [8] xbits = to_binary_array(x); 142 | let [8] ybits = 0; 143 | 144 | for (let i = 0; i != sizeof(xbits) - n; ++i) 145 | { 146 | ybits[i] = xbits[i + n]; 147 | } 148 | 149 | let y = from_binary_array(ybits); 150 | } 151 | 152 | function y = bit8_shift_right(&x, &n) 153 | { 154 | let [8] xbits = to_binary_array(x); 155 | let [8] ybits = 0; 156 | 157 | for (let i = 0; i != sizeof(xbits) - n; ++i) 158 | { 159 | ybits[i + n] = xbits[i]; 160 | } 161 | 162 | let y = from_binary_array(ybits); 163 | } 164 | 165 | function x = rand() 166 | { 167 | let x = __rand(); 168 | } 169 | 170 | -------------------------------------------------------------------------------- /src/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_H 2 | #define MEMORY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "typesystem.h" 10 | 11 | class Memory 12 | { 13 | public: 14 | enum class Content 15 | { 16 | EMPTY, 17 | NAMED, 18 | TEMP, 19 | REFERENCED, 20 | }; 21 | 22 | private: 23 | struct Cell 24 | { 25 | std::string identifier; 26 | std::string scope; 27 | Content content{Content::EMPTY}; 28 | TypeSystem::Type type; 29 | int value{0}; 30 | bool synced{false}; 31 | 32 | void clear(); 33 | bool empty() const 34 | { 35 | return content == Content::EMPTY; 36 | } 37 | 38 | int size() const 39 | { 40 | return type.size(); 41 | } 42 | 43 | private: 44 | using Members = std::tuple; 48 | 49 | std::stack d_backupStack; 50 | }; 51 | 52 | std::vector d_memory; 53 | std::map>> d_aliasMap; 54 | 55 | int d_maxAddr{0}; 56 | 57 | public: 58 | Memory(size_t sz): 59 | d_memory(sz) 60 | {} 61 | 62 | size_t size() const; 63 | int getTemp(std::string const &scope, TypeSystem::Type type); 64 | int getTemp(std::string const &scope, int const sz = 1); 65 | int getTempBlock(std::string const &scope, int const sz); 66 | int allocate(std::string const &ident, std::string const &scope, TypeSystem::Type type); 67 | void addAlias(int const addr, std::string const &ident, std::string const &scope); 68 | void removeAlias(int const addr, std::string const &ident, std::string const &scope); 69 | 70 | int find(std::string const &ident, std::string const &scope, bool const includeEnclosedScopes = true) const; 71 | int sizeOf(int const addr) const; 72 | int sizeOf(std::string const &ident, std::string const &scope) const; 73 | void freeTemps(std::string const &scope); 74 | void freeLocals(std::string const &scope); 75 | void markAsTemp(int const addr); 76 | void rename(int const addr, std::string const &ident, std::string const &scope); 77 | bool isTemp(int const addr) const; 78 | int value(int const addr) const; 79 | int &value(int const addr); 80 | bool valueKnown(int const addr) const; 81 | void setValueUnknown(int const addr); 82 | void setSync(int const addr, bool val); 83 | bool isSync(int const addr) const; 84 | std::string identifier(int const addr) const; 85 | std::string scope(int const addr) const; 86 | TypeSystem::Type type(int const addr) const; 87 | TypeSystem::Type type(std::string const &ident, std::string const &scope) const; 88 | std::vector cellsInScope(std::string const &scope) const; 89 | size_t cellsRequired() const 90 | { 91 | return d_maxAddr; 92 | } 93 | 94 | void dump() const; 95 | 96 | private: 97 | int findFree(int sz = 1); 98 | void place(TypeSystem::Type type, int const addr, bool const recursive = false); 99 | 100 | template 101 | void freeIf(Predicate &&pred); 102 | }; 103 | 104 | inline size_t Memory::size() const 105 | { 106 | return d_memory.size(); 107 | } 108 | 109 | template 110 | void Memory::freeIf(Predicate&& pred) 111 | { 112 | for (size_t idx = 0; idx < d_memory.size(); ++idx) 113 | { 114 | Cell &cell = d_memory[idx]; 115 | if (pred(cell)) 116 | { 117 | for (int offset = 1; offset < cell.size(); ++offset) 118 | { 119 | Cell &referenced = d_memory[idx + offset]; 120 | referenced.clear(); 121 | } 122 | cell.clear(); 123 | idx += cell.size(); 124 | } 125 | } 126 | 127 | } 128 | 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /src/bfgenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef BFGENERATOR_H 2 | #define BFGENERATOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class BFGenerator 9 | { 10 | size_t d_pointer{0}; 11 | size_t d_maxCellValue; 12 | std::function f_getTemp; 13 | std::function f_getTempBlock; 14 | std::function f_getMemSize; 15 | 16 | std::map d_profile; 17 | 18 | public: 19 | BFGenerator(size_t maxCellValue = 0xff): 20 | d_maxCellValue(maxCellValue) 21 | {} 22 | 23 | size_t getPointerIndex() const 24 | { 25 | return d_pointer; 26 | } 27 | 28 | template 29 | void setTempRequestFn(GetTemp &&getTemp) 30 | { 31 | f_getTemp = std::forward(getTemp); 32 | } 33 | 34 | template 35 | void setTempBlockRequestFn(GetTempBlock &&getTempBlock) 36 | { 37 | f_getTempBlock = std::forward(getTempBlock); 38 | } 39 | 40 | template 41 | void setMemSizeRequestFn(GetMemSize &&getMemSize) 42 | { 43 | f_getMemSize = std::forward(getMemSize); 44 | } 45 | 46 | std::string movePtr(int const addr); 47 | std::string scan(int const addr); 48 | std::string print(int const addr); 49 | std::string random(int const addr); 50 | std::string fetchElement(int const arrStart, int const arrSize, int const index, int const ret); 51 | std::string setToValue(int const addr, int const val); 52 | std::string setToValue(int const start, int const val, size_t const n); 53 | std::string setToValuePlus(int const addr, int const val); 54 | std::string setToValuePlus(int const addr, int const val, size_t const n); 55 | std::string assign(int const lhs, int const rhs); 56 | std::string assignElement(int const arrStart, int const arrSize, int const index, int const val); 57 | std::string addTo(int const target, int const rhs); 58 | std::string addConst(int const target, int const amount); 59 | std::string incr(int const target); 60 | std::string decr(int const target); 61 | std::string safeDecr(int const target, int const underflow); 62 | std::string subtractFrom(int const target, int const rhs); 63 | std::string multiply(int const lhs, int const rhs, int const result); 64 | std::string multiplyBy(int const target, int const rhs); 65 | std::string power(int const lhs, int const rhs, int const result); 66 | std::string powerBy(int const lhs, int const rhs); 67 | std::string divmod(int const num, int const denom, int const divResult, int const modResult); 68 | std::string equal(int const lhs, int const rhs, int const result); 69 | std::string notEqual(int const lhs, int const rhs, int const result); 70 | std::string greater(int const lhs, int const rhs, int const result); 71 | std::string less(int const lhs, int const rhs, int const result); 72 | std::string greaterOrEqual(int const lhs, int const rhs, int const result); 73 | std::string lessOrEqual(int const lhs, int const rhs, int const result); 74 | std::string logicalNot(int const operand); 75 | std::string logicalNot(int const operand, int const result); 76 | std::string logicalAnd(int const lhs, int const rhs, int const result); 77 | std::string logicalAnd(int const lhs, int const rhs); 78 | std::string logicalOr(int const lhs, int const rhs, int const result); 79 | std::string logicalOr(int const lhs, int const rhs); 80 | 81 | inline std::map const &profile() const 82 | { 83 | return d_profile; 84 | } 85 | 86 | private: 87 | 88 | template 89 | void validateAddr__(std::string const &function, int first, Rest&& ... rest) const 90 | { 91 | if (first < 0 || ((rest < 0) || ...)) 92 | { 93 | std::cerr << "Fatal internal error in call to " << function 94 | << ": negative address.\n\n" 95 | << "Compilation terminated\n"; 96 | std::exit(1); 97 | } 98 | 99 | int const sz = f_getMemSize(); 100 | if (first > sz || (((int)rest >= sz) || ...)) 101 | { 102 | std::cerr << "Fatal internal error in call to " << function 103 | << ": address out of bounds.\n\n" 104 | << "Compilation terminated\n"; 105 | 106 | std::exit(1); 107 | } 108 | } 109 | 110 | }; 111 | 112 | #endif //BFGENERATOR_H 113 | -------------------------------------------------------------------------------- /bfx_examples/tictactoe.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | const OFFSET = 10; 4 | const PLAYER1 = 1; 5 | const PLAYER2 = 2; 6 | 7 | function drawEmptyGrid() 8 | { 9 | let [] l1 = " | | "; 10 | let [] l2 = "___|___|___"; 11 | let [OFFSET] offset = ' '; 12 | 13 | prints(offset); println(l1); 14 | prints(offset); println(l1); 15 | prints(offset); println(l2); 16 | prints(offset); println(l1); 17 | prints(offset); println(l1); 18 | prints(offset); println(l2); 19 | prints(offset); println(l1); 20 | prints(offset); println(l1); 21 | prints(offset); println(l1); 22 | } 23 | 24 | struct Grid 25 | { 26 | [9] cells; 27 | [9] line; 28 | [9] column; 29 | } 30 | 31 | function grid = newGrid() 32 | { 33 | let [struct Grid] grid; 34 | 35 | grid.cells = 0; 36 | 37 | grid.line = #{2, 2, 2, 38 | 5, 5, 5, 39 | 8, 8, 8}; 40 | 41 | grid.column = #{2, 6, 10, 42 | 2, 6, 10, 43 | 2, 6, 10}; 44 | 45 | for (let i = 0; i != 9; ++i) 46 | grid.column[i] += OFFSET; 47 | } 48 | 49 | function drawGrid(&grid) 50 | { 51 | for (let i = 0; i != 9; ++i) 52 | { 53 | cursor_to(grid.line[i], grid.column[i]); 54 | let cell = grid.cells[i]; 55 | if (cell == 0) 56 | printc(i + 1 + '0'); 57 | else if (cell == PLAYER1) 58 | printc('X'); 59 | else if (cell == PLAYER2) 60 | printc('O'); 61 | } 62 | } 63 | 64 | function c = getMove() 65 | { 66 | let c; scans(c); 67 | if (c < '1' || c > '9') 68 | c = 0; 69 | else 70 | c -= '0'; 71 | } 72 | 73 | function playerTurn(&grid, &name, &line, &playerID) 74 | { 75 | cursor_to(line++, 1); 76 | let move = 0; 77 | while (!move) 78 | { 79 | prints(name); 80 | prints(", please make your move (1-9): "); 81 | move = getMove(); 82 | if (move == 0) 83 | { 84 | println("Invalid move."); 85 | line += 2; 86 | } 87 | else if (grid.cells[move - 1]) 88 | { 89 | println("Cell not available."); 90 | move = 0; 91 | line += 2; 92 | } 93 | } 94 | 95 | grid.cells[move - 1] = playerID; 96 | } 97 | 98 | function w = won(&grid, &playerID) 99 | { 100 | let w = false; 101 | // Check rows/columns 102 | for (let i = 0; i != 3; ++i) 103 | { 104 | if (grid.cells[i * 3 + 0] == playerID && 105 | grid.cells[i * 3 + 1] == playerID && 106 | grid.cells[i * 3 + 2] == playerID) 107 | { 108 | w = true; 109 | return; 110 | } 111 | 112 | if (grid.cells[i] == playerID && 113 | grid.cells[i + 3] == playerID && 114 | grid.cells[i + 6] == playerID) 115 | { 116 | w = true; 117 | return; 118 | } 119 | } 120 | 121 | // Check diagonals 122 | if (grid.cells[0] == playerID && 123 | grid.cells[4] == playerID && 124 | grid.cells[8] == playerID) 125 | { 126 | w = true; 127 | return; 128 | } 129 | 130 | if (grid.cells[2] == playerID && 131 | grid.cells[4] == playerID && 132 | grid.cells[6] == playerID) 133 | { 134 | w = true; 135 | return; 136 | } 137 | } 138 | 139 | const NAME_BUFFER_SIZE = 10; 140 | 141 | function main() 142 | { 143 | let [NAME_BUFFER_SIZE] p1; 144 | let [NAME_BUFFER_SIZE] p2; 145 | 146 | prints("Player 1, please enter your name: "); 147 | scans(p1); 148 | prints("Player 2, please enter your name: "); 149 | scans(p2); 150 | 151 | let [struct Grid] grid = newGrid(); 152 | 153 | clear_screen(); 154 | drawEmptyGrid(); 155 | 156 | drawGrid(grid); 157 | let line = 11; 158 | let nMoves = 0; 159 | while (nMoves < 9) 160 | { 161 | let player = 1 + (nMoves % 2); 162 | let [NAME_BUFFER_SIZE] name = 0; 163 | if (player == 1) 164 | strcpy(name, p1); 165 | else 166 | strcpy(name, p2); 167 | 168 | playerTurn(grid, name, line, player); 169 | drawGrid(grid); 170 | if (won(grid, player)) 171 | { 172 | cursor_to(line, 1); 173 | prints(name); 174 | println(" won!"); 175 | return; 176 | } 177 | 178 | ++nMoves; 179 | } 180 | 181 | cursor_to(line, 1); 182 | println("It's a tie!"); 183 | } 184 | 185 | -------------------------------------------------------------------------------- /bfx_examples/tictactoe_cpu.bfx: -------------------------------------------------------------------------------- 1 | // Compiler and run with --random! 2 | 3 | include "std.bfx" 4 | 5 | const OFFSET = 10; 6 | const PLAYER = 1; 7 | const CPU = 2; 8 | 9 | function drawEmptyGrid() 10 | { 11 | let [] l1 = " | | "; 12 | let [] l2 = "___|___|___"; 13 | let [OFFSET] offset = ' '; 14 | 15 | prints(offset); println(l1); 16 | prints(offset); println(l1); 17 | prints(offset); println(l2); 18 | prints(offset); println(l1); 19 | prints(offset); println(l1); 20 | prints(offset); println(l2); 21 | prints(offset); println(l1); 22 | prints(offset); println(l1); 23 | prints(offset); println(l1); 24 | } 25 | 26 | struct Grid 27 | { 28 | [9] cells; 29 | [9] line; 30 | [9] column; 31 | } 32 | 33 | function grid = newGrid() 34 | { 35 | let [struct Grid] grid; 36 | 37 | grid.cells = 0; 38 | 39 | grid.line = #{2, 2, 2, 40 | 5, 5, 5, 41 | 8, 8, 8}; 42 | 43 | grid.column = #{2, 6, 10, 44 | 2, 6, 10, 45 | 2, 6, 10}; 46 | 47 | for (let i = 0; i != 9; ++i) 48 | grid.column[i] += OFFSET; 49 | } 50 | 51 | function drawGrid(&grid) 52 | { 53 | for (let i = 0; i != 9; ++i) 54 | { 55 | cursor_to(grid.line[i], grid.column[i]); 56 | let cell = grid.cells[i]; 57 | if (cell == 0) 58 | printc(i + 1 + '0'); 59 | else if (cell == PLAYER) 60 | printc('X'); 61 | else if (cell == CPU) 62 | printc('O'); 63 | } 64 | } 65 | 66 | function c = getMove() 67 | { 68 | let c; scans(c); 69 | if (c < '1' || c > '9') 70 | c = 0; 71 | else 72 | c -= '0'; 73 | } 74 | 75 | function playerTurn(&grid, &name, &line) 76 | { 77 | cursor_to(line++, 1); 78 | let move = 0; 79 | while (!move) 80 | { 81 | prints(name); 82 | prints(", please make your move (1-9): "); 83 | move = getMove(); 84 | if (move == 0) 85 | { 86 | println("Invalid move."); 87 | line += 2; 88 | } 89 | else if (grid.cells[move - 1]) 90 | { 91 | println("Cell not available."); 92 | move = 0; 93 | line += 2; 94 | } 95 | } 96 | 97 | grid.cells[move - 1] = PLAYER; 98 | } 99 | 100 | function cpuTurn(&grid, &line) 101 | { 102 | let move = 0; 103 | while (true) 104 | { 105 | move = rand() % 9; 106 | if (grid.cells[move] == 0) 107 | break; 108 | 109 | move = 0; 110 | } 111 | 112 | cursor_to(line++, 1); 113 | prints("CPU chose "); printd(move + 1); endl(); 114 | grid.cells[move] = CPU; 115 | } 116 | 117 | 118 | function w = won(&grid, &playerID) 119 | { 120 | let w = false; 121 | // Check rows/columns 122 | for (let i = 0; i != 3; ++i) 123 | { 124 | if (grid.cells[i * 3 + 0] == playerID && 125 | grid.cells[i * 3 + 1] == playerID && 126 | grid.cells[i * 3 + 2] == playerID) 127 | { 128 | w = true; 129 | return; 130 | } 131 | 132 | if (grid.cells[i] == playerID && 133 | grid.cells[i + 3] == playerID && 134 | grid.cells[i + 6] == playerID) 135 | { 136 | w = true; 137 | return; 138 | } 139 | } 140 | 141 | // Check diagonals 142 | if (grid.cells[0] == playerID && 143 | grid.cells[4] == playerID && 144 | grid.cells[8] == playerID) 145 | { 146 | w = true; 147 | return; 148 | } 149 | 150 | if (grid.cells[2] == playerID && 151 | grid.cells[4] == playerID && 152 | grid.cells[6] == playerID) 153 | { 154 | w = true; 155 | return; 156 | } 157 | } 158 | 159 | const NAME_BUFFER_SIZE = 10; 160 | 161 | function main() 162 | { 163 | let [NAME_BUFFER_SIZE] name; 164 | prints("Human, please enter your name: "); 165 | scans(name); 166 | 167 | 168 | let [struct Grid] grid = newGrid(); 169 | clear_screen(); 170 | drawEmptyGrid(); 171 | drawGrid(grid); 172 | 173 | let line = 11; 174 | let nMoves = 0; 175 | while (nMoves < 9) 176 | { 177 | let player = 1 + (nMoves % 2); 178 | if (player == PLAYER) 179 | playerTurn(grid, name, line); 180 | else 181 | cpuTurn(grid, line); 182 | 183 | drawGrid(grid); 184 | if (won(grid, player)) 185 | { 186 | cursor_to(line, 1); 187 | prints(name); 188 | println(" won!"); 189 | return; 190 | } 191 | 192 | ++nMoves; 193 | } 194 | 195 | cursor_to(line, 1); 196 | println("It's a tie!"); 197 | } 198 | 199 | -------------------------------------------------------------------------------- /bfx_examples/gol.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | /* Conway's Game of Life 4 | 5 | - Define the grid dimensions by setting the parameters below. 6 | - Prepare a file with 0's and 1's (characters) which denote 7 | the initial conditions (0 = dead, 1 = alive). E.g. for a 8 | 5x5 grid: 9 | 10 | 00000 11 | 01110 12 | 11100 13 | 00000 14 | 00000 15 | 16 | - The examplefiles golglider5x5.txt, golglider10x10.txt and 17 | golpenta11x18.txt are included in the examples folder. 18 | 19 | - Compile the program: 20 | $ bfx gol.bf bfx_examples/gol.bfx 21 | 22 | - Run the interpreter while redirecting the prepared file to stdin: 23 | $ bfint gol.bf < bfx_examples/gol/golpenta11x18.txt 24 | 25 | - The output becomes very slow very fast for larger grids. However, 26 | if you insist on grids larger than 250 cells in total, use the 27 | compiler flag -t int16 to allow for larger BF-cells (necessary for 28 | array indexing). Run bfint in int16-mode as well. 29 | */ 30 | 31 | 32 | const GRID_WIDTH = 11; 33 | const GRID_HEIGHT = 18; 34 | const ARRAY_SIZE = 198; // should be GRID_WIDTH * GRID_HEIGHT 35 | 36 | // Don't alter values for DEAD and ALIVE! 37 | const DEAD = 0; 38 | const ALIVE = 1; 39 | 40 | struct Grid 41 | { 42 | [ARRAY_SIZE] cells, count; 43 | }; 44 | 45 | function i = toIndex(&row, &col) 46 | { 47 | let i = row * GRID_WIDTH + col; 48 | } 49 | 50 | function g = getGridFromStdin() 51 | { 52 | let [struct Grid] g; 53 | g.count = 0; 54 | 55 | for (let row = 0; row != GRID_HEIGHT; ++row) 56 | { 57 | for (let col = 0; col != GRID_WIDTH; ++col) 58 | { 59 | let c = scanc() - '0'; 60 | while (c != ALIVE && c != DEAD) 61 | c = scanc() - '0'; 62 | 63 | g.cells[toIndex(row, col)] = c; 64 | } 65 | } 66 | } 67 | 68 | function dispGrid(&g) 69 | { 70 | printc('+'); 71 | for (let col = 0; col != 2 * GRID_WIDTH; ++col) 72 | printc('-'); 73 | printc('+'); 74 | endl(); 75 | 76 | for (let row = 0; row != GRID_HEIGHT; ++row) 77 | { 78 | printc('|'); 79 | for (let col = 0; col != GRID_WIDTH; ++col) 80 | { 81 | if (g.cells[toIndex(row, col)] == ALIVE) 82 | prints("<>"); 83 | else 84 | prints(" "); 85 | } 86 | printc('|'); 87 | endl(); 88 | } 89 | 90 | printc('+'); 91 | for (let col = 0; col != 2 * GRID_WIDTH; ++col) 92 | printc('-'); 93 | printc('+'); 94 | endl(); 95 | prints("Iterations: "); 96 | printd(g.count); endl(); 97 | } 98 | 99 | function row = rowAbove(row) 100 | { 101 | if (row == 0) 102 | row = GRID_HEIGHT - 1; 103 | else 104 | --row; 105 | } 106 | 107 | function row = rowBelow(row) 108 | { 109 | if (row == GRID_HEIGHT - 1) 110 | row = 0; 111 | else 112 | ++row; 113 | } 114 | 115 | function col = colLeft(col) 116 | { 117 | if (col == 0) 118 | col = GRID_WIDTH - 1; 119 | else 120 | --col; 121 | } 122 | 123 | function col = colRight(col) 124 | { 125 | if (col == GRID_WIDTH - 1) 126 | col = 0; 127 | else 128 | ++col; 129 | } 130 | 131 | function nb = neighbors(row, col) 132 | { 133 | let [8] nb; 134 | 135 | nb[0] = toIndex(rowAbove(row), col); // N 136 | nb[1] = toIndex(row, colRight(col)); // E 137 | nb[2] = toIndex(rowBelow(row), col); // S 138 | nb[3] = toIndex(row, colLeft(col)); // W 139 | 140 | nb[4] = toIndex(rowAbove(row), colRight(col)); // NE 141 | nb[5] = toIndex(rowBelow(row), colRight(col)); // SE 142 | nb[6] = toIndex(rowBelow(row), colLeft(col)); // SW 143 | nb[7] = toIndex(rowAbove(row), colLeft(col)); // NW 144 | } 145 | 146 | function n = countLiveNeighbors(&grid, &row, &col) 147 | { 148 | let [8] nb = neighbors(row, col); 149 | let n = 0; 150 | for (let i = 0; i != sizeof(nb); ++i) 151 | n += grid.cells[nb[i]]; 152 | } 153 | 154 | function iterate(&grid) 155 | { 156 | let [struct Grid] newGrid; 157 | for (let row = 0; row != GRID_HEIGHT; ++row) 158 | { 159 | for (let col = 0; col != GRID_WIDTH; ++col) 160 | { 161 | let n = countLiveNeighbors(grid, row, col); 162 | let idx = toIndex(row, col); 163 | if (grid.cells[idx] == ALIVE) 164 | newGrid.cells[idx] = !(n < 2 || n > 3); 165 | else 166 | newGrid.cells[idx] = (n == 3); 167 | } 168 | } 169 | 170 | grid.cells = newGrid.cells; 171 | ++grid.count; 172 | } 173 | 174 | function main() 175 | { 176 | static_assert(ARRAY_SIZE == GRID_WIDTH * GRID_HEIGHT, 177 | "Array-size not equal to width * height."); 178 | 179 | let [] g = getGridFromStdin(); 180 | 181 | clear_screen(); 182 | while* (true) 183 | { 184 | cursor_home(); 185 | dispGrid(g); 186 | iterate(g); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/lexer: -------------------------------------------------------------------------------- 1 | %class-name Scanner 2 | %filenames scanner 3 | %lex-source = "lex_flexcpp_generated.cc" 4 | 5 | ident [_a-zA-Z][_a-zA-Z0-9]* 6 | blanks [[:blank:]\n] 7 | 8 | %x string 9 | %x character 10 | %x eolcomment 11 | %x ccomment 12 | %x test 13 | %x test_content 14 | %x escape_test_content 15 | 16 | %% 17 | 18 | {blanks} // ignore 19 | 20 | "include" return CompilerBase::INCLUDE; 21 | "function" return CompilerBase::FUNCTION; 22 | "let" return CompilerBase::LET; 23 | "struct" return CompilerBase::STRUCT; 24 | "global" return CompilerBase::GLOBAL; 25 | "const" return CompilerBase::CONST; 26 | "for" return CompilerBase::FOR; 27 | "if" return CompilerBase::IF; 28 | "else" return CompilerBase::ELSE; 29 | "while" return CompilerBase::WHILE; 30 | "switch" return CompilerBase::SWITCH; 31 | "case" return CompilerBase::CASE; 32 | "default" return CompilerBase::DEFAULT; 33 | "break" return CompilerBase::BREAK; 34 | "continue" return CompilerBase::CONTINUE; 35 | "return" return CompilerBase::RETURN; 36 | "static_assert" return CompilerBase::ASSERT; 37 | "++" return CompilerBase::INC; 38 | "--" return CompilerBase::DEC; 39 | "+=" return CompilerBase::ADD; 40 | "-=" return CompilerBase::SUB; 41 | "*=" return CompilerBase::MUL; 42 | "/=" return CompilerBase::DIV; 43 | "%=" return CompilerBase::MOD; 44 | "^=" return CompilerBase::POW; 45 | "/=%" return CompilerBase::DIVMOD; 46 | "%=/" return CompilerBase::MODDIV; 47 | "<=" return CompilerBase::LE; 48 | ">=" return CompilerBase::GE; 49 | "==" return CompilerBase::EQ; 50 | "!=" return CompilerBase::NE; 51 | "&&" return CompilerBase::AND; 52 | "||" return CompilerBase::OR; 53 | "__scan" return CompilerBase::SCAN; 54 | "__print" return CompilerBase::PRINT; 55 | "__rand" return CompilerBase::RAND; 56 | "sizeof" return CompilerBase::SIZEOF; 57 | 58 | [[:digit:]]+ return CompilerBase::NUM; 59 | {ident} return CompilerBase::IDENT; 60 | 61 | "'"."'" { 62 | setMatched(matched().substr(1, 1)); 63 | return CompilerBase::CHR; 64 | } 65 | "'\\"."'" { 66 | char escaped = escape(matched()[2]); 67 | setMatched(std::string(1, escaped)); 68 | return CompilerBase::CHR; 69 | } 70 | 71 | \" { 72 | pushStartCondition(StartCondition_::string); 73 | } 74 | 75 | "//" { 76 | pushStartCondition(StartCondition_::eolcomment); 77 | } 78 | 79 | "/*" { 80 | ++d_nestedCommentLevel; 81 | pushStartCondition(StartCondition_::ccomment); 82 | } 83 | 84 | 85 | "@start_test" { 86 | pushStartCondition(StartCondition_::test); 87 | return CompilerBase::START_TEST; 88 | } 89 | 90 | .|\n|\r return matched()[0]; 91 | 92 | { 93 | \" { 94 | popStartCondition(); 95 | setMatched(matched().substr(0, matched().size() - 1)); 96 | return CompilerBase::STR; 97 | } 98 | 99 | "\\". { 100 | char escaped = escape(matched().back()); 101 | setMatched(matched().substr(0, matched().size() - 2) + escaped); 102 | more(); 103 | } 104 | 105 | . more(); 106 | } 107 | 108 | { 109 | \n|\r popStartCondition(); 110 | . // ignore 111 | } 112 | 113 | { 114 | "/*" { 115 | ++d_nestedCommentLevel; 116 | } 117 | "*/" { 118 | if (--d_nestedCommentLevel == 0) 119 | popStartCondition(); 120 | } 121 | .|\n|\r 122 | } 123 | 124 | { 125 | 126 | "<"|">" return matched()[0]; 127 | {ident} return CompilerBase::IDENT; 128 | 129 | \" { 130 | more(); 131 | pushStartCondition(StartCondition_::string); 132 | } 133 | ^"```input"{blanks}*\n { 134 | pushStartCondition(StartCondition_::test_content); 135 | return CompilerBase::INPUT; 136 | } 137 | 138 | ^"```expect"{blanks}*\n { 139 | pushStartCondition(StartCondition_::test_content); 140 | return CompilerBase::EXPECT; 141 | } 142 | 143 | "@end_test" { 144 | popStartCondition(); 145 | return CompilerBase::END_TEST; 146 | } 147 | 148 | "//" { 149 | pushStartCondition(StartCondition_::eolcomment); 150 | } 151 | 152 | "/*" { 153 | ++d_nestedCommentLevel; 154 | pushStartCondition(StartCondition_::ccomment); 155 | } 156 | 157 | .|\n // ignore 158 | } // test 159 | 160 | 161 | { 162 | 163 | \\\n // ignore \ at end of line 164 | 165 | "\\". { 166 | setMatched(std::string(1, escape(matched()[1]))); 167 | return CompilerBase::CHR; 168 | } 169 | 170 | "${" { 171 | pushStartCondition(StartCondition_::escape_test_content); 172 | } 173 | 174 | 175 | ^"```"{blanks}*\n { 176 | popStartCondition(); 177 | return CompilerBase::END_TEST_CONTENT; 178 | } 179 | 180 | .|\n { 181 | return CompilerBase::CHR; 182 | } 183 | 184 | } // test_content 185 | 186 | { 187 | 188 | "}" { 189 | char c = escapeTestContent(matched().substr(0, matched().size() - 1)); 190 | setMatched(std::string(1, c)); 191 | popStartCondition(); 192 | return CompilerBase::CHR; 193 | } 194 | 195 | . more(); 196 | 197 | } // escape -------------------------------------------------------------------------------- /bfx_examples/snake.bfx: -------------------------------------------------------------------------------- 1 | include "std.bfx" 2 | 3 | /* Snake 4 | 5 | Simple implementation of the classic Snake game. 6 | 7 | - Optionally edit the controls and screen-dimensions below. 8 | 9 | - The speed at which it runs, depends on your system. Edit 10 | SLEEP_TIME to modify framerate (higher = slower). If 255 11 | is not enough, consider compiling and running with 12 | --type int16 to unlock larger values. Don't forget to specify 13 | a maximum value for the RNG, e.g. 14 | 15 | $ bfx --random --type int16 -o snake.bf bfx_examples/snake.bfx 16 | $ bfint --random --rand-max 255 --type int16 --gaming snake.bf 17 | 18 | - Compile with --random 19 | - Run with --random --gaming 20 | 21 | Screenshot: 22 | +------------------------+ 23 | | | 24 | | | 25 | | | 26 | | <><><> | 27 | | <> | 28 | | <> | 29 | | ** | 30 | | () | 31 | | | 32 | | | 33 | | | 34 | | | 35 | +------------------------+ 36 | Score: 4 37 | 38 | */ 39 | 40 | // Controls 41 | const UP_KEY = 'w'; 42 | const DOWN_KEY = 's'; 43 | const LEFT_KEY = 'a'; 44 | const RIGHT_KEY = 'd'; 45 | 46 | // Dimensions 47 | const GRID_WIDTH = 12; 48 | const GRID_HEIGHT = 12; 49 | const ARRAY_SIZE = 144; 50 | const MAX_LENGTH = 15; 51 | 52 | // Do not alter 53 | const N = 0; 54 | const E = 1; 55 | const S = 2; 56 | const W = 3; 57 | 58 | struct Game 59 | { 60 | [ARRAY_SIZE] snake; 61 | snakeLength, dir, fruitIndex; 62 | score; 63 | }; 64 | 65 | function i = toIndex(&row, &col) 66 | { 67 | let i = row * GRID_WIDTH + col; 68 | } 69 | 70 | function rc = toRowCol(&index) 71 | { 72 | let [2] rc; 73 | rc[0] = index / GRID_WIDTH; 74 | rc[1] = index % GRID_HEIGHT; 75 | } 76 | 77 | function row = rowAbove(row) 78 | { 79 | if (row == 0) 80 | row = GRID_HEIGHT - 1; 81 | else 82 | --row; 83 | } 84 | 85 | function row = rowBelow(row) 86 | { 87 | if (row == GRID_HEIGHT - 1) 88 | row = 0; 89 | else 90 | ++row; 91 | } 92 | 93 | function col = colLeft(col) 94 | { 95 | if (col == 0) 96 | col = GRID_WIDTH - 1; 97 | else 98 | --col; 99 | } 100 | 101 | function col = colRight(col) 102 | { 103 | if (col == GRID_WIDTH - 1) 104 | col = 0; 105 | else 106 | ++col; 107 | } 108 | 109 | function game = initGame() 110 | { 111 | let [struct Game] game; 112 | game.snake = 0; 113 | game.snakeLength = 2; 114 | game.dir = E; 115 | 116 | game.snake[0] = toIndex(GRID_HEIGHT / 2, 1); 117 | game.snake[1] = toIndex(GRID_HEIGHT / 2, 0); 118 | 119 | spawnFruit(game); 120 | } 121 | 122 | 123 | function drawEmptyGrid() 124 | { 125 | clear_screen(); 126 | printc('+'); 127 | for (let col = 0; col != GRID_WIDTH; ++col) 128 | prints("--"); 129 | printc('+'); 130 | endl(); 131 | 132 | 133 | for (let row = 0; row != GRID_HEIGHT; ++row) 134 | { 135 | printc('|'); 136 | for (let col = 0; col != GRID_WIDTH; ++col) 137 | prints(" "); 138 | 139 | printc('|'); 140 | endl(); 141 | } 142 | 143 | printc('+'); 144 | for (let col = 0; col != GRID_WIDTH; ++col) 145 | prints("--"); 146 | printc('+'); 147 | endl(); 148 | } 149 | 150 | function drawGame(&game) 151 | { 152 | // Draw head 153 | let [2] head = toRowCol(game.snake[0]); 154 | cursor_to(2 + head[0], 2 + 2 * head[1]); 155 | prints("**"); 156 | 157 | // Draw body 158 | for (let i = 1; i != game.snakeLength; ++i) 159 | { 160 | let [2] body = toRowCol(game.snake[i]); 161 | cursor_to(2 + body[0], 2 + 2 * body[1]); 162 | prints("<>"); 163 | } 164 | 165 | // Draw Fruit 166 | let [2] fruit = toRowCol(game.fruitIndex); 167 | cursor_to(2 + fruit[0], 2 + 2 * fruit[1]); 168 | prints("()"); 169 | 170 | // Draw score 171 | cursor_to(GRID_HEIGHT + 3, 1); 172 | prints("Score: "); 173 | printd(game.score); 174 | } 175 | 176 | function iterate(&game) 177 | { 178 | // Update head 179 | let [] oldSnake = game.snake; 180 | let row = game.snake[0] / GRID_WIDTH; 181 | let col = game.snake[0] % GRID_WIDTH; 182 | 183 | switch (game.dir) 184 | { 185 | case N: game.snake[0] = toIndex(rowAbove(row), col); 186 | case E: game.snake[0] = toIndex(row, colRight(col)); 187 | case S: game.snake[0] = toIndex(rowBelow(row), col); 188 | case W: game.snake[0] = toIndex(row, colLeft(col)); 189 | } 190 | 191 | // Check if fruit is eaten 192 | if (game.fruitIndex == game.snake[0]) 193 | eatFruit(game); 194 | 195 | // Update body 196 | for (let i = 1; i != game.snakeLength; ++i) 197 | game.snake[i] = oldSnake[i - 1]; 198 | } 199 | 200 | function handleInput(&game) 201 | { 202 | let c = scanc(); 203 | if (!c) 204 | return; 205 | 206 | switch (c) 207 | { 208 | case UP_KEY: 209 | { 210 | if (game.dir != S) 211 | game.dir = N; 212 | } 213 | case DOWN_KEY: 214 | { 215 | if (game.dir != N) 216 | game.dir = S; 217 | } 218 | case LEFT_KEY: 219 | { 220 | if (game.dir != E) 221 | game.dir = W; 222 | } 223 | case RIGHT_KEY: 224 | { 225 | if (game.dir != W) 226 | game.dir = E; 227 | } 228 | } 229 | } 230 | 231 | function spawnFruit(&game) 232 | { 233 | let row = rand() % GRID_HEIGHT; 234 | let col = rand() % GRID_WIDTH; 235 | game.fruitIndex = toIndex(row, col); 236 | } 237 | 238 | function eatFruit(&game) 239 | { 240 | ++game.score; 241 | if (game.snakeLength < MAX_LENGTH) 242 | { 243 | let tailIndex = game.snake[game.snakeLength - 1]; 244 | game.snake[game.snakeLength++] = tailIndex; 245 | } 246 | 247 | spawnFruit(game); 248 | } 249 | 250 | function dead = died(&game) 251 | { 252 | let dead = false; 253 | for (let i = 1; i != game.snakeLength; ++i) 254 | { 255 | if (game.snake[0] == game.snake[i]) 256 | { 257 | dead = true; 258 | return; 259 | } 260 | } 261 | } 262 | 263 | function ans = restartPrompt() 264 | { 265 | prints("Press 'r' to restart or 'q' to quit"); 266 | 267 | let ans = 0; 268 | while (!ans) 269 | { 270 | ans = scanc(); 271 | switch (ans) 272 | { 273 | case 'r': break; 274 | case 'q': break; 275 | } 276 | } 277 | } 278 | 279 | function endScreen(&game) 280 | { 281 | cursor_to(GRID_HEIGHT + 3, 1); 282 | clear_line(); 283 | cursor_down(); 284 | println("GAME OVER!"); 285 | prints("Final score: "); printd(game.score); endl(); 286 | } 287 | 288 | function t = speedPrompt() 289 | { 290 | cursor_to(GRID_HEIGHT / 2, GRID_WIDTH / 2); 291 | prints("Enter speed (1-5)"); 292 | let t = 0; 293 | while (!t) 294 | { 295 | t = scanc(); 296 | if (t < '1' || t > '5') 297 | continue; 298 | 299 | t = 250 - (t - '1') * 40; 300 | } 301 | } 302 | 303 | function sleep(n) 304 | { 305 | for* (let i = 0; i != n; ++i) 306 | {} 307 | } 308 | 309 | function main() 310 | { 311 | static_assert(ARRAY_SIZE == GRID_WIDTH * GRID_HEIGHT, 312 | "Array-size not equal to width * height."); 313 | 314 | static_assert(MAX_LENGTH <= ARRAY_SIZE, 315 | "Maximum snake-length must be less or equal to width * height."); 316 | 317 | while* (true) 318 | { 319 | let [struct Game] game = initGame(); 320 | let sleepTime = speedPrompt(); 321 | 322 | while* (true) 323 | { 324 | if (died(game)) 325 | break; 326 | 327 | handleInput(game); 328 | drawEmptyGrid(); 329 | drawGame(game); 330 | iterate(game); 331 | sleep(sleepTime); 332 | } 333 | 334 | endScreen(game); 335 | if (restartPrompt() == 'q') 336 | break; 337 | } 338 | 339 | clear_screen(); 340 | cursor_to(GRID_HEIGHT / 2, GRID_WIDTH / 2); 341 | prints("Press any key to exit"); 342 | } 343 | -------------------------------------------------------------------------------- /std/stdio.bfx: -------------------------------------------------------------------------------- 1 | // STD IO 2 | 3 | /* Provided IO functions: 4 | 5 | OUTPUT 6 | 7 | printc(x) - Print byte as ASCII character 8 | printd(x) - Print byte as decimal (max 3 digits) 9 | printd_4(x) - Print byte as decimal (max 4 digits) 10 | prints(str) - Print string (stop at NULL or end of the string) 11 | println(str) - Same as prints() but with appended newline 12 | print_vec(v) - Print formatted vector, including newline: (v1, v2, v3, ..., vn) 13 | endl() - Print a newline (same as printc('\n')) 14 | 15 | INPUT 16 | 17 | scanc() - Read a single byte from stdin 18 | scand() - Read at most 3 bytes from stdin and convert to decimal (0-999) 19 | scand_4() - Read at most 4 bytes from stdin and convert to decimal (0-9999) 20 | scans(buf) - Read string from stdin. sizeof(buf) determines maximum number of bytes read 21 | 22 | CONVERSIONS 23 | 24 | to_int(str) - Converts string to int, max 3 digits (0-999) 25 | to_int_4(str) - Converts string to int, max 4 digits (0-9999) 26 | to_string(x) - Converts int to string, max 3 digits (0-999) 27 | to_string_4(x) - Converts int to string, max 4 digits (0-9999) 28 | to_binary_str(x) - Converts number to 8-bit binary representation (as string) 29 | to_hex_str(x) - Converts number to 8-bit hexadecimal representation (as string) 30 | 31 | CURSOR/SCREEN MANIPULATION 32 | 33 | cursor_left(n) - Move cursor left by n (default = 1) 34 | cursor_right(n) - Move cursor right by n (default = 1) 35 | cursor_up(n) - Move cursor up by n (default = 1) 36 | cursor_down(n) - Move cursor down by n (default = 1) 37 | cursor_home() - Move cursor to top-left of the screen 38 | cursor_to(line, column) - Move cursor to given position 39 | clear_screen() - Clear the entire screen 40 | clear_line() - Clear the current line 41 | */ 42 | 43 | function printc(&c) 44 | { 45 | __print(c); 46 | } 47 | 48 | function prints(&str) 49 | { 50 | let i = 0; 51 | while (i < sizeof(str) && str[i] != 0) 52 | printc(str[i++]); 53 | } 54 | 55 | function println(&x) 56 | { 57 | prints(x); 58 | endl(); 59 | } 60 | 61 | function printd(&x) 62 | { 63 | let [3] str = to_string(x); 64 | for (let &d: str) 65 | if (d) printc(d); 66 | } 67 | 68 | function printb(b) 69 | { 70 | if (b) 71 | prints("true"); 72 | else 73 | prints("false"); 74 | } 75 | 76 | function printd_4(&x) 77 | { 78 | let [4] str = to_string_4(x); 79 | for (let d: str) 80 | if (d) printc(d); 81 | } 82 | 83 | 84 | function print_vec(&v) 85 | { 86 | printc('('); 87 | for(let i = 0; i != sizeof(v); ++i) 88 | { 89 | printd(v[i]); 90 | if (i != sizeof(v) - 1) 91 | { 92 | printc(','); 93 | printc(' '); 94 | } 95 | } 96 | printc(')'); 97 | endl(); 98 | } 99 | 100 | function endl() 101 | { 102 | printc('\n'); 103 | } 104 | 105 | function c = scanc() 106 | { 107 | let c = __scan(); 108 | } 109 | 110 | function result = scand() 111 | { 112 | let [3] input = 0; 113 | scans(input); 114 | 115 | let [3] powers = #{1, 10, 100}; 116 | let result = 0; 117 | let j = 0; 118 | 119 | for (let i = 0; i != 3; ++i) 120 | { 121 | let x = input[2 - i]; 122 | if (x != 0) 123 | result += (x - '0') * powers[j++]; 124 | } 125 | } 126 | 127 | function result = scand_4() 128 | { 129 | let [4] input = 0; 130 | scans(input); 131 | 132 | let [4] powers = #{1, 10, 100, 1000}; 133 | let result = 0; 134 | let j = 0; 135 | 136 | for (let i = 0; i != 4; ++i) 137 | { 138 | let x = input[3 - i]; 139 | if (x != 0) 140 | result += (x - '0') * powers[j++]; 141 | } 142 | } 143 | 144 | function scans(&buf) 145 | { 146 | let i = 0; 147 | let done = 0; 148 | while (!done) 149 | { 150 | let c = scanc(); 151 | if (c != '\n') 152 | { 153 | if (i < sizeof(buf)) 154 | buf[i++] = c; 155 | } 156 | else 157 | { 158 | buf[i] = 0; 159 | done = 1; 160 | } 161 | } 162 | } 163 | 164 | function result = to_int(&str) 165 | { 166 | static_assert(sizeof(str) <= 3, "Argument to to_int can have at most 3 digits."); 167 | 168 | let nDigits = sizeof(str); 169 | if (nDigits > 3) 170 | nDigits = 3; 171 | 172 | let [3] powers = #{1, 10, 100}; 173 | let result = 0; 174 | let j = 0; 175 | for (let i = 0; i != nDigits; ++i) 176 | { 177 | let x = str[nDigits - 1 - i]; 178 | if (x != 0) 179 | result += (x - '0') * powers[j++]; 180 | } 181 | } 182 | 183 | function result = to_int_4(&str) 184 | { 185 | static_assert(sizeof(str) <= 3, "Argument to to_int_4 can have at most 4 digits."); 186 | 187 | let nDigits = sizeof(str); 188 | if (nDigits > 4) 189 | nDigits = 4; 190 | 191 | let [4] powers = #{1, 10, 100, 1000}; 192 | let result = 0; 193 | let j = 0; 194 | for(let i = 0; i != nDigits; ++i) 195 | { 196 | let x = str[nDigits - 1 - i]; 197 | if (x != 0) 198 | result += (x - '0') * powers[j++]; 199 | } 200 | } 201 | 202 | function str = to_binary_str(x) 203 | { 204 | let [] powers = #{128, 64, 32, 16, 8, 4, 2, 1}; 205 | let [] str = "00000000"; 206 | for(let i = 0; i < sizeof(powers); ++i) 207 | { 208 | let p = powers[i]; 209 | if (x >= p) 210 | { 211 | str[i] = '1'; 212 | x -= p; 213 | } 214 | } 215 | } 216 | 217 | function str = to_hex_str(x) 218 | { 219 | let lowNibble = (x /=% 16); 220 | let [] c = "0123456789abcdef"; 221 | let [] str = #{'0', 'x', c[x], c[lowNibble]}; 222 | } 223 | 224 | function str = to_string(x) 225 | { 226 | let hundreds = (x %=/ 100); 227 | let tens = (x %=/ 10); 228 | let ones = x; 229 | 230 | let idx = 0; 231 | let [3]str = 0; 232 | 233 | if (hundreds != 0) 234 | { 235 | str[idx++] = hundreds + '0'; 236 | str[idx++] = tens + '0'; 237 | } 238 | else if (tens != 0) 239 | { 240 | str[idx++] = tens + '0'; 241 | } 242 | 243 | str[idx] = ones + '0'; 244 | } 245 | 246 | function str = to_string_4(x) 247 | { 248 | let thousands = (x %=/ 1000); 249 | let hundreds = (x %=/ 100); 250 | let tens = (x %=/ 10); 251 | let ones = x; 252 | 253 | let idx = 0; 254 | let [4] str = 0; 255 | 256 | if (thousands != 0) 257 | { 258 | str[idx++] = thousands + '0'; 259 | str[idx++] = hundreds + '0'; 260 | str[idx++] = tens + '0'; 261 | } 262 | else if (hundreds != 0) 263 | { 264 | str[idx++] = hundreds + '0'; 265 | str[idx++] = tens + '0'; 266 | } 267 | else if (tens != 0) 268 | { 269 | str[idx++] = tens + '0'; 270 | } 271 | 272 | str[idx] = ones + '0'; 273 | } 274 | 275 | function clear_screen() 276 | { 277 | cursor_home(); 278 | printc(27); 279 | prints("[0J"); 280 | } 281 | 282 | function clear_line() 283 | { 284 | printc(13); 285 | printc(27); 286 | prints("[0K"); 287 | } 288 | 289 | function cursor_to(&line, &column) 290 | { 291 | printc(27); 292 | printc('['); 293 | prints(to_string(line)); 294 | printc(';'); 295 | prints(to_string(column)); 296 | printc('H'); 297 | } 298 | 299 | function cursor_up(n) 300 | { 301 | printc(27); 302 | printc('['); 303 | prints(to_string(n)); 304 | printc('A'); 305 | } 306 | 307 | function cursor_up() 308 | { 309 | printc(27); 310 | prints("[1A"); 311 | } 312 | 313 | function cursor_down(n) 314 | { 315 | printc(27); 316 | printc('['); 317 | prints(to_string(n)); 318 | printc('B'); 319 | } 320 | 321 | function cursor_down() 322 | { 323 | printc(27); 324 | prints("[1B"); 325 | } 326 | 327 | function cursor_right(n) 328 | { 329 | printc(27); 330 | printc('['); 331 | prints(to_string(n)); 332 | printc('C'); 333 | } 334 | 335 | function cursor_right() 336 | { 337 | printc(27); 338 | prints("[1C"); 339 | } 340 | 341 | function cursor_left(n) 342 | { 343 | printc(27); 344 | printc('['); 345 | prints(to_string(n)); 346 | printc('D'); 347 | } 348 | 349 | function cursor_left() 350 | { 351 | printc(27); 352 | prints("[1D"); 353 | } 354 | 355 | function cursor_home() 356 | { 357 | printc(27); 358 | prints("[H"); 359 | } 360 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "compiler.h" 6 | 7 | 8 | void printHelp(std::string const &progName) 9 | { 10 | std::cout << "Usage: " << progName << " [options] \n" 11 | << "Options:\n" 12 | << "-h, --help Display this text.\n" 13 | << "-t, --type [Type] Specify the number of bytes per BF-cell, where [Type] is one of\n" 14 | " int8, int16 and int32 (int8 by default).\n" 15 | << "-I [path to folder] Specify additional include-path.\n" 16 | << " This option may appear multiple times to specify multiple folders.\n" 17 | << "-O0 Do NOT do any constant expression evaluation.\n" 18 | << "-O1 Do constant expression evaluation (default).\n" 19 | << "--max-unroll-iterations [N]\n" 20 | << " Specify the maximum number of loop-iterations that will be unrolled.\n" 21 | << " Defaults to 20.\n" 22 | << "--test [file] Produce test-files and write a list of generated files to [file], to\n" 23 | << " be used by bfint for unit-testing.\n" 24 | << "--random Enable random number generation (generates the ?-symbol).\n" 25 | << " Your interpreter must support this extension!\n" 26 | << "--profile [file] Write the memory profile to a file. In this file, the number of visits\n" 27 | << " to each of the cells is listed.\n" 28 | << "--no-bcr Disable break/continue/return statements for more compact output.\n" 29 | << "--no-multiple-inclusion-warning\n" 30 | << " Do not warn when a file is included more than once, or when files \n" 31 | << " with duplicate names are included.\n" 32 | << "--no-assert-warning\n" 33 | << " Do not warn when static_assert is used in non-constant context.\n" 34 | << "-o [file, stdout] Specify the output stream/file (default stdout).\n\n" 35 | << "Example: " << progName << " -o program.bf -O1 -I ~/my_bfx_project -t int16 program.bfx\n"; 36 | } 37 | 38 | std::pair parseCmdLine(std::vector const &args) 39 | { 40 | Compiler::Options opt; 41 | 42 | size_t idx = 1; 43 | while (idx < args.size()) 44 | { 45 | if (args[idx] == "-h" || args[idx] == "--help") 46 | { 47 | return {opt, 1}; 48 | } 49 | else if (args[idx] == "-t" || args[idx] == "--type") 50 | { 51 | if (idx == args.size() - 1) 52 | { 53 | std::cerr << "ERROR: No argument passed to option \'-t\'.\n"; 54 | return {opt, 1}; 55 | } 56 | 57 | static std::map const getType{ 58 | {"int8", Compiler::CellType::INT8}, 59 | {"int16", Compiler::CellType::INT16}, 60 | {"int32", Compiler::CellType::INT32} 61 | }; 62 | 63 | auto tolower = [](std::string str)->std::string 64 | { 65 | std::transform(str.begin(), str.end(), str.begin(), 66 | [](unsigned char c){ return std::tolower(c); }); 67 | return str; 68 | }; 69 | 70 | std::string arg = tolower(args[idx + 1]); 71 | try 72 | { 73 | opt.cellType = getType.at(arg); 74 | idx += 2; 75 | } 76 | catch (std::out_of_range const&) 77 | { 78 | std::cerr << "ERROR: Invalid argument passed to option \'-t\'\n"; 79 | return {opt, 1}; 80 | } 81 | } 82 | else if (args[idx] == "-I") 83 | { 84 | if (idx == args.size() - 1) 85 | { 86 | std::cerr << "ERROR: No argument passed to option \'-I\'.\n"; 87 | return {opt, 1}; 88 | } 89 | 90 | opt.includePaths.push_back(args[idx + 1]); 91 | idx += 2; 92 | } 93 | else if (args[idx] == "-o") 94 | { 95 | if (idx == args.size() - 1) 96 | { 97 | std::cerr << "ERROR: No argument passed to option \'-o\'.\n"; 98 | return {opt, 1}; 99 | } 100 | 101 | if (args[idx + 1] == "stdout") 102 | { 103 | opt.outStream = &std::cout; 104 | idx += 2; 105 | } 106 | else 107 | { 108 | std::string const &fname = args[idx + 1]; 109 | static std::ofstream file; 110 | file.open(fname); 111 | if (!file.good()) 112 | { 113 | std::cerr << "ERROR: could not open output-file " << fname << ".\n"; 114 | return {opt, 1}; 115 | } 116 | 117 | opt.outStream = &file; 118 | idx += 2; 119 | } 120 | } 121 | else if (args[idx] == "-O0") 122 | { 123 | opt.constEvalAllowed = false; 124 | ++idx; 125 | } 126 | else if (args[idx] == "-O1") 127 | { 128 | ++idx; 129 | } 130 | else if (args[idx] == "--max-unroll-iterations") 131 | { 132 | if (idx == args.size() - 1) 133 | { 134 | std::cerr << "ERROR: No argument passed to option \'--max-unroll-iterations\'.\n"; 135 | return {opt, 1}; 136 | } 137 | 138 | try { 139 | opt.maxUnrollIterations = std::stoi(args[idx + 1]); 140 | idx += 2; 141 | } 142 | catch (...) { 143 | std::cerr << "Could not convert argument to --max-unroll-iterations to integer."; 144 | return {opt, 1}; 145 | } 146 | } 147 | else if (args[idx] == "--test") 148 | { 149 | if (idx == args.size() - 1) 150 | { 151 | std::cerr << "ERROR: No filename passed to option \'--test\'.\n"; 152 | return {opt, 1}; 153 | } 154 | 155 | opt.testFile = args[idx + 1]; 156 | idx += 2; 157 | 158 | } 159 | else if (args[idx] == "--random") 160 | { 161 | opt.randomEnabled = true; 162 | ++idx; 163 | } 164 | else if (args[idx] == "--profile") 165 | { 166 | if (idx == args.size() - 1) 167 | { 168 | std::cerr << "ERROR: No filename passed to option \'--profile\'.\n"; 169 | return {opt, 1}; 170 | } 171 | 172 | opt.profileFile = args[idx + 1]; 173 | idx += 2; 174 | 175 | } 176 | else if (args[idx] == "--no-bcr") 177 | { 178 | opt.bcrEnabled = false; 179 | ++idx; 180 | } 181 | else if (args[idx] == "--no-multiple-inclusion-warning") 182 | { 183 | opt.includeWarningEnabled = false; 184 | ++idx; 185 | } 186 | else if (args[idx] == "--no-assert-warning") 187 | { 188 | opt.assertWarningEnabled = false; 189 | ++idx; 190 | } 191 | else if (idx == args.size() - 1) 192 | { 193 | opt.bfxFile = args.back(); 194 | break; 195 | } 196 | else 197 | { 198 | std::cerr << "Unknown option " << args[idx] << ".\n"; 199 | return {opt, 1}; 200 | } 201 | } 202 | 203 | if (!opt.outStream || !opt.outStream->good()) 204 | { 205 | std::cerr << "ERROR: Output file not set. Use -o to specifiy stdout or a file.\n"; 206 | return {opt, 1}; 207 | } 208 | 209 | if (idx > (args.size() - 1)) 210 | { 211 | std::cerr << "ERROR: No input (.bfx) file specified.\n"; 212 | return {opt, 1}; 213 | } 214 | 215 | return {opt, 0}; 216 | } 217 | 218 | 219 | int main(int argc, char **argv) try 220 | { 221 | auto result = parseCmdLine(std::vector(argv, argv + argc)); 222 | if (result.second == 1) 223 | { 224 | printHelp(argv[0]); 225 | return 1; 226 | } 227 | 228 | Compiler c(result.first); 229 | int err = c.compile(); 230 | 231 | if (err) return err; 232 | 233 | c.write(); 234 | } 235 | catch (std::string const &msg) 236 | { 237 | std::cerr << msg << '\n'; 238 | } 239 | -------------------------------------------------------------------------------- /src/interpreter/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "bfint.h" 6 | 7 | void printHelp(std::string const &progName) 8 | { 9 | std::cout << "Usage: " << progName << " [options] \n" 10 | << "Options:\n" 11 | << "-h, --help Display this text.\n" 12 | << "-t, --type [Type] Specify the number of bytes per BF-cell, where [Type] is one of\n" 13 | " int8, int16 and int32 (int8 by default).\n" 14 | << "-n [N] Specify the number of cells (30,000 by default).\n" 15 | << "--test [file] Run the tests specified by the file (generated by bfx --test)\n" 16 | #ifdef USE_CURSES 17 | << "--gaming Enable gaming-mode.\n" 18 | << "--gaming-help Display additional information about gaming-mode.\n" 19 | #endif 20 | << "--random Enable Random Brainf*ck extension (support ?-symbol)\n" 21 | << "--rand-max [N] Specifiy maximum value returned by RNG.\n" 22 | << " Defaults to maximum supported value of cell-type\n" 23 | << "--no-random-warning Don't display a warning when ? occurs without running --random.\n\n" 24 | << "Example: " << progName << " --random -t int16 -o output.txt program.bf\n"; 25 | } 26 | 27 | void printGamingHelp(std::string const &progName) 28 | { 29 | std::cout << 30 | "\nWhen " << progName << " is run with the --gaming option, all writes and reads are performed\n" 31 | "by ncurses, in order to establish non-blocking IO. This allowes you to run games written in\n" 32 | "BF that require keyboard-input (',' in BF) to be processed immediately, without waiting for\n" 33 | "the user to press enter. If no key was pressed, a 0 is stored to the current BF-cell.\n\n" 34 | 35 | "In the default non-gaming mode, it is possible to write ANSI escape sequences to the output,\n" 36 | " which may be used to modify the cursor position, clear the screen, or change the color. A\n" 37 | "subset of these sequences has been implemented and will be translated to sequences of\n" 38 | "ncurses-calls to mimic this behavior:\n\n" 39 | " - ESC[nA ==> Move the cursor up n lines.\n" 40 | " - ESC[nB ==> Move the cursor down n lines.\n" 41 | " - ESC[nC ==> Move the cursor right n steps.\n" 42 | " - ESC[nD ==> Move the cursor left n steps (erasing present characters).\n" 43 | " - ESC[n;mH ==> Move the cursor to row n, column m.\n" 44 | " - ESC[H ==> Move the cursor to the top-left of the screen.\n" 45 | " - ESC[nK ==> n = 0: clear from cursor to end-of-line.\n" 46 | " n = 1: clear from cursor to start-of-line.\n" 47 | " n = 2: clear the entire line\n" 48 | " - ESC[nJ ==> n = 0: clear from cursor to bottom of screen.\n" 49 | " ==> n = 1: clear all lines above cursor, including current line.\n" 50 | " ==> n = 2: clear entire screen.\n\n"; 51 | } 52 | 53 | Options parseCmdLine(std::vector const &args) 54 | { 55 | Options opt; 56 | 57 | size_t idx = 1; 58 | while (idx < args.size()) 59 | { 60 | if (args[idx] == "-h" || args[idx] == "--help") 61 | { 62 | opt.err = 1; 63 | return opt; 64 | } 65 | 66 | else if (args[idx] == "-t" || args[idx] == "--type") 67 | { 68 | if (idx == args.size() - 1) 69 | { 70 | std::cerr << "ERROR: No argument passed to option \'-t\'.\n"; 71 | opt.err = 1; 72 | return opt; 73 | } 74 | 75 | static std::map const getType{ 76 | {"int8", CellType::INT8}, 77 | {"int16", CellType::INT16}, 78 | {"int32", CellType::INT32} 79 | }; 80 | 81 | auto tolower = [](std::string str)->std::string 82 | { 83 | std::transform(str.begin(), str.end(), str.begin(), 84 | [](unsigned char c){ return std::tolower(c); }); 85 | return str; 86 | }; 87 | 88 | std::string arg = tolower(args[idx + 1]); 89 | try 90 | { 91 | opt.cellType = getType.at(arg); 92 | idx += 2; 93 | } 94 | catch (std::out_of_range const&) 95 | { 96 | std::cerr << "ERROR: Invalid argument passed to option \'-t\'\n"; 97 | opt.err = 1; 98 | return opt; 99 | } 100 | } 101 | else if (args[idx] == "-n") 102 | { 103 | if (idx == args.size() - 1) 104 | { 105 | std::cerr << "ERROR: No argument passed to option \'-n\'.\n"; 106 | opt.err = 1; 107 | return opt; 108 | } 109 | try 110 | { 111 | opt.tapeLength = std::stoi(args[idx + 1]); 112 | if (opt.tapeLength <= 0) 113 | { 114 | std::cerr << "ERROR: tape length must be a positive integer.\n"; 115 | opt.err = 1; 116 | return opt; 117 | } 118 | idx += 2; 119 | } 120 | catch (std::invalid_argument const&) 121 | { 122 | std::cerr << "ERROR: Invalid argument passed to option \'-n\'\n"; 123 | opt.err = 1; 124 | return opt; 125 | } 126 | } 127 | else if (args[idx] == "--test") 128 | { 129 | if (idx == args.size() - 1) 130 | { 131 | std::cerr << "ERROR: No filename passed to option \'--test\'.\n"; 132 | return opt; 133 | } 134 | 135 | opt.testFile = args[idx + 1]; 136 | idx += 2; 137 | } 138 | else if (args[idx] == "--random") 139 | { 140 | opt.randomEnabled = true; 141 | ++idx; 142 | } 143 | else if (args[idx] == "--rand-max") 144 | { 145 | if (idx == args.size() - 1) 146 | { 147 | std::cerr << "ERROR: No argument passed to option '--rand-max'.\n"; 148 | opt.err = 1; 149 | return opt; 150 | } 151 | try 152 | { 153 | opt.randMax = std::stoi(args[idx + 1]); 154 | if (opt.randMax <= 0) 155 | { 156 | std::cerr << "ERROR: rand-max must be a positive integer.\n"; 157 | opt.err = 1; 158 | return opt; 159 | } 160 | 161 | idx += 2; 162 | } 163 | catch (std::invalid_argument const&) 164 | { 165 | std::cerr << "ERROR: Invalid argument passed to option --rand-max\n"; 166 | opt.err = 1; 167 | return opt; 168 | } 169 | } 170 | #ifdef USE_CURSES 171 | else if (args[idx] == "--gaming") 172 | { 173 | opt.gamingMode = true; 174 | ++idx; 175 | } 176 | else if (args[idx] == "--gaming-help") 177 | { 178 | opt.gamingMode = true; 179 | opt.err = 1; 180 | return opt; 181 | } 182 | #endif 183 | else if (args[idx] == "--no-random-warning") 184 | { 185 | opt.randomWarningEnabled = false; 186 | ++idx; 187 | } 188 | 189 | else if (idx == args.size() - 1) 190 | { 191 | opt.bfFile = args.back(); 192 | break; 193 | } 194 | else 195 | { 196 | std::cerr << "Unknown option " << args[idx] << ".\n"; 197 | opt.err = 1; 198 | return opt; 199 | } 200 | } 201 | 202 | 203 | if (idx > (args.size() - 1)) 204 | { 205 | std::cerr << "ERROR: No input (.bf) file specified.\n"; 206 | opt.err = 1; 207 | return opt; 208 | } 209 | 210 | return opt; 211 | } 212 | 213 | 214 | int main(int argc, char **argv) 215 | try 216 | { 217 | Options opt = parseCmdLine(std::vector(argv, argv + argc)); 218 | if (opt.err == 1) 219 | { 220 | #ifdef USE_CURSES 221 | if (opt.gamingMode) 222 | printGamingHelp(argv[0]); 223 | else 224 | printHelp(argv[0]); 225 | #else 226 | printHelp(argv[0]); 227 | #endif 228 | return 1; 229 | } 230 | 231 | if (opt.randMax > 0 && !opt.randomEnabled) 232 | { 233 | std::cerr << "Warning: a value for rand-max was specified but the random extension was not " 234 | "enabled. Use --random to enable this feature.\n"; 235 | } 236 | 237 | BFInterpreter bfint(opt); 238 | return bfint.run(); 239 | } 240 | catch (std::string const &msg) 241 | { 242 | std::cerr << msg << '\n'; 243 | } 244 | -------------------------------------------------------------------------------- /src/memory.cc: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | 3 | void Memory::Cell::clear() 4 | { 5 | identifier.clear(); 6 | scope.clear(); 7 | content = Content::EMPTY; 8 | type = TypeSystem::Type{}; 9 | value = 0; 10 | synced = false; 11 | } 12 | 13 | int Memory::findFree(int const sz) 14 | { 15 | for (size_t start = 0; start != d_memory.size() - sz; ++start) 16 | { 17 | bool done = true; 18 | for (int offset = 0; offset != sz; ++offset) 19 | { 20 | if (!d_memory[start + offset].empty()) 21 | { 22 | done = false; 23 | break; 24 | } 25 | } 26 | 27 | if (done) return start; 28 | } 29 | 30 | d_memory.resize(d_memory.size() + sz); 31 | return findFree(sz); 32 | } 33 | 34 | int Memory::getTemp(std::string const &scope, TypeSystem::Type type) 35 | { 36 | return allocate("", scope, type); 37 | } 38 | 39 | int Memory::getTemp(std::string const &scope, int const sz) 40 | { 41 | return getTemp(scope, TypeSystem::Type(sz)); 42 | } 43 | 44 | int Memory::getTempBlock(std::string const &scope, int const sz) 45 | { 46 | int start = findFree(sz); 47 | for (int i = 0; i != sz; ++i) 48 | { 49 | Cell &cell = d_memory[start + i]; 50 | cell.clear(); 51 | cell.scope = scope; 52 | cell.type = TypeSystem::Type(1); 53 | cell.content = Content::TEMP; 54 | } 55 | 56 | return start; 57 | } 58 | 59 | int Memory::allocate(std::string const &ident, std::string const &scope, TypeSystem::Type type) 60 | { 61 | assert(type.defined() && "Trying to allocate undefined type"); 62 | 63 | if (!ident.empty() && find(ident, scope, false) != -1) 64 | return -1; 65 | 66 | int const addr = findFree(type.size()); 67 | if (addr + type.size() > d_maxAddr) 68 | d_maxAddr = addr + type.size(); 69 | 70 | Cell &cell = d_memory[addr]; 71 | cell.clear(); 72 | cell.identifier = ident; 73 | cell.scope = scope; 74 | cell.content = ident.empty() ? Content::TEMP : Content::NAMED; 75 | cell.type = type; 76 | 77 | place(type, addr); 78 | return addr; 79 | } 80 | 81 | void Memory::addAlias(int const addr, std::string const &ident, std::string const &scope) 82 | { 83 | assert(find(ident, scope, false) == -1 && "alias identifier already exists"); 84 | d_aliasMap[addr].push_back({ident, scope}); 85 | } 86 | 87 | void Memory::removeAlias(int const addr, std::string const &ident, std::string const &scope) 88 | { 89 | assert(d_aliasMap.find(addr) != d_aliasMap.end() && "trying to erase non existent alias"); 90 | 91 | std::erase_if(d_aliasMap[addr], 92 | [&](auto const &pr){ 93 | return pr.first == ident && pr.second == scope; 94 | }); 95 | } 96 | 97 | void Memory::place(TypeSystem::Type type, int const addr, bool const recursive) 98 | { 99 | if (type.isIntType()) 100 | { 101 | for (int i = 1; i != type.size(); ++i) 102 | { 103 | Cell &cell = d_memory[addr + i]; 104 | cell.clear(); 105 | cell.type = TypeSystem::Type(1); 106 | cell.content = Content::REFERENCED; 107 | } 108 | return; 109 | } 110 | 111 | if (recursive) 112 | { 113 | Cell &cell = d_memory[addr]; 114 | cell.clear(); 115 | cell.content = Content::REFERENCED; 116 | cell.type = type; 117 | } 118 | 119 | for (auto const &f: type.fields()) 120 | { 121 | if (f.type.isStructType()) 122 | { 123 | place(f.type, addr + f.offset, true); 124 | continue; 125 | } 126 | 127 | Cell &cell = d_memory[addr + f.offset]; 128 | cell.clear(); 129 | cell.type = f.type; 130 | cell.content = Content::REFERENCED; 131 | 132 | for (int i = 1; i != f.type.size(); ++i) 133 | { 134 | Cell &cell = d_memory[addr + f.offset + i]; 135 | cell.clear(); 136 | cell.type = TypeSystem::Type(1); 137 | cell.content = Content::REFERENCED; 138 | } 139 | } 140 | } 141 | 142 | int Memory::find(std::string const &ident, std::string const &scope, bool const includeEnclosedScopes) const 143 | { 144 | if (includeEnclosedScopes) 145 | { 146 | int maxLength = -1; 147 | int result = -1; 148 | auto const match = 149 | [&](std::string const &otherIdent, std::string const &otherScope, int const addr) 150 | { 151 | if (otherIdent == ident && scope.find(otherScope) == 0) 152 | { 153 | if (static_cast(otherScope.size()) > maxLength) 154 | { 155 | result = addr; 156 | maxLength = otherScope.size(); 157 | } 158 | } 159 | }; 160 | 161 | // Search memory for this identifier in scope 162 | for (size_t idx = 0; idx != d_memory.size(); ++idx) 163 | { 164 | Cell const &cell = d_memory[idx]; 165 | match(cell.identifier, cell.scope, idx); 166 | } 167 | 168 | // Identifier might be an alias 169 | for (auto const &pr1: d_aliasMap) 170 | { 171 | auto const &[addr, vec] = pr1; 172 | for (auto const &pr2: vec) 173 | { 174 | auto const &[aliasIdent, aliasScope] = pr2; 175 | match(aliasIdent, aliasScope, addr); 176 | } 177 | } 178 | 179 | return result; 180 | } 181 | else 182 | { 183 | // Try to find perfect match in memory 184 | auto const it = std::find_if(d_memory.begin(), d_memory.end(), 185 | [&](Cell const &cell) 186 | { 187 | return cell.identifier == ident && scope == cell.scope; 188 | }); 189 | 190 | if (it != d_memory.end()) 191 | { 192 | // Found a match, return address: 193 | return it - d_memory.begin(); 194 | } 195 | 196 | // Not in memory: try to find a match in the alias-map 197 | for (auto const &pr1: d_aliasMap) 198 | { 199 | auto const &[addr, vec] = pr1; 200 | for (auto const &pr2: vec) 201 | { 202 | auto const &[aliasIdent, aliasScope] = pr2; 203 | if (ident == aliasIdent && scope == aliasScope) 204 | return addr; 205 | } 206 | } 207 | 208 | // Also not an alias -> not found 209 | return -1; 210 | } 211 | 212 | assert(false && "unreachable"); 213 | } 214 | 215 | void Memory::freeTemps(std::string const &scope) 216 | { 217 | freeIf([&](Cell const &cell){ 218 | return cell.content == Content::TEMP && 219 | cell.scope == scope; 220 | }); 221 | } 222 | 223 | void Memory::freeLocals(std::string const &scope) 224 | { 225 | // Remove all aliases from this scope 226 | for (auto &pr: d_aliasMap) 227 | { 228 | std::erase_if(pr.second, 229 | [&](auto const &pr2) -> bool 230 | { 231 | return pr2.second == scope; 232 | }); 233 | } 234 | 235 | // Free all memory in this scope 236 | freeIf([&](Cell const &cell){ 237 | return cell.scope == scope; 238 | } 239 | ); 240 | } 241 | 242 | int Memory::sizeOf(int const addr) const 243 | { 244 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 245 | Cell const &cell = d_memory[addr]; 246 | assert(!cell.empty() && "Requested size of empty address"); 247 | return cell.size(); 248 | } 249 | 250 | int Memory::sizeOf(std::string const &ident, std::string const &scope) const 251 | { 252 | int const addr = find(ident, scope); 253 | return (addr >= 0) ? d_memory[addr].size() : 0; 254 | } 255 | 256 | void Memory::markAsTemp(int const addr) 257 | { 258 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 259 | Cell &cell = d_memory[addr]; 260 | 261 | cell.identifier = ""; 262 | cell.content = Content::TEMP; 263 | } 264 | 265 | void Memory::rename(int const addr, std::string const &ident, std::string const &scope) 266 | { 267 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 268 | Cell &cell = d_memory[addr]; 269 | cell.identifier = ident; 270 | cell.scope = scope; 271 | 272 | cell.content = Content::NAMED; 273 | } 274 | 275 | std::string Memory::identifier(int const addr) const 276 | { 277 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 278 | return d_memory[addr].identifier; 279 | } 280 | 281 | std::string Memory::scope(int const addr) const 282 | { 283 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 284 | return d_memory[addr].scope; 285 | } 286 | 287 | bool Memory::isTemp(int const addr) const 288 | { 289 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 290 | return d_memory[addr].content == Content::TEMP; 291 | } 292 | 293 | TypeSystem::Type Memory::type(int const addr) const 294 | { 295 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 296 | return d_memory[addr].type; 297 | } 298 | 299 | TypeSystem::Type Memory::type(std::string const &ident, std::string const &scope) const 300 | { 301 | int const addr = find(ident, scope); 302 | return d_memory[addr].type; 303 | } 304 | 305 | int Memory::value(int const addr) const 306 | { 307 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 308 | return d_memory[addr].value; 309 | } 310 | 311 | int &Memory::value(int const addr) 312 | { 313 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 314 | return d_memory[addr].value; 315 | } 316 | 317 | bool Memory::valueKnown(int const addr) const 318 | { 319 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 320 | return d_memory[addr].value != -1; 321 | } 322 | 323 | void Memory::setValueUnknown(int const addr) 324 | { 325 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 326 | d_memory[addr].value = -1; 327 | setSync(addr, false); 328 | } 329 | 330 | void Memory::setSync(int const addr, bool sync) 331 | { 332 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 333 | d_memory[addr].synced = sync; 334 | } 335 | 336 | bool Memory::isSync(int const addr) const 337 | { 338 | assert(addr >= 0 && addr < (int)d_memory.size() && "address out of bounds"); 339 | return d_memory[addr].synced; 340 | } 341 | 342 | std::vector Memory::cellsInScope(std::string const &scope) const 343 | { 344 | std::vector result; 345 | for (int i = 0; i != d_maxAddr; ++i) 346 | { 347 | if (scope.find(d_memory[i].scope) == 0) 348 | result.push_back(i); 349 | } 350 | 351 | for (auto const &pr1: d_aliasMap) 352 | { 353 | auto const &[addr, vec] = pr1; 354 | for (auto const &pr2: vec) 355 | { 356 | if (scope.find(pr2.second) == 0) 357 | result.push_back(addr); 358 | } 359 | } 360 | 361 | return result; 362 | } 363 | 364 | 365 | void Memory::dump() const 366 | { 367 | static std::string const contentStrings[] = 368 | { 369 | "EMPTY", 370 | "NAMED", 371 | "TEMP", 372 | "REFERENCED", 373 | }; 374 | 375 | std::cerr << "addr | var | scope | type | content | value | synced | \n"; 376 | for (int i = 0; i != d_maxAddr; ++i) 377 | { 378 | Cell const &c = d_memory[i]; 379 | if (c.content == Content::EMPTY) 380 | continue; 381 | 382 | std::cerr << i << "\t" << c.identifier << '\t' << c.scope << '\t' 383 | << c.type.name() << '\t' << contentStrings[static_cast(c.content)] << '\t' 384 | << c.value << '\t' << (c.synced ? "SYNCED" : "DESYNCED") << '\n'; 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /src/scannerbase.h: -------------------------------------------------------------------------------- 1 | // Generated by Flexc++ V2.07.07 on Thu, 07 Jul 2022 17:06:03 -46033506 2 | 3 | #ifndef ScannerBASE_H_INCLUDED 4 | #define ScannerBASE_H_INCLUDED 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | 15 | class ScannerBase 16 | { 17 | // idx: rule, value: tail length (NO_INCREMENTS if no tail) 18 | typedef std::vector VectorInt; 19 | 20 | static size_t const s_unavailable = std::numeric_limits::max(); 21 | 22 | enum 23 | { 24 | AT_EOF = -1 25 | }; 26 | 27 | protected: 28 | enum Leave_ 29 | {}; 30 | 31 | enum class ActionType_ 32 | { 33 | CONTINUE, // transition succeeded, go on 34 | ECHO_CH, // echo ch itself (d_matched empty) 35 | ECHO_FIRST, // echo d_matched[0], push back the rest 36 | MATCH, // matched a rule 37 | RETURN, // no further continuation, lex returns 0. 38 | }; 39 | 40 | enum class PostEnum_ 41 | { 42 | END, // postCode called when lex_() ends 43 | POP, // postCode called after switching files 44 | RETURN, // postCode called when lex_() returns 45 | WIP // postCode called when a non-returning rule 46 | // was matched 47 | }; 48 | 49 | public: 50 | // $insert startcondenum 51 | enum class StartCondition_{ 52 | INITIAL, 53 | string, 54 | character, 55 | eolcomment, 56 | ccomment, 57 | test, 58 | test_content, 59 | escape_test_content, 60 | }; 61 | 62 | private: 63 | struct FinalData 64 | { 65 | size_t rule; 66 | size_t length; 67 | }; 68 | struct Final 69 | { 70 | FinalData std; 71 | FinalData bol; 72 | }; 73 | 74 | // class Input encapsulates all input operations. 75 | // Its member get() returns the next input character 76 | // $insert inputInterface 77 | 78 | class Input 79 | { 80 | std::deque d_deque; // pending input chars 81 | std::istream *d_in; // ptr for easy streamswitching 82 | size_t d_lineNr; // line count 83 | 84 | public: 85 | Input(); 86 | // iStream: dynamically allocated 87 | Input(std::istream *iStream, size_t lineNr = 1); 88 | size_t get(); // the next range 89 | void reRead(size_t ch); // push back 'ch' (if < 0x100) 90 | // push back str from idx 'fmIdx' 91 | void reRead(std::string const &str, size_t fmIdx); 92 | size_t lineNr() const 93 | { 94 | return d_lineNr; 95 | } 96 | size_t nPending() const 97 | { 98 | return d_deque.size(); 99 | } 100 | void setPending(size_t size) 101 | { 102 | d_deque.erase(d_deque.begin(), d_deque.end() - size); 103 | } 104 | void close() // force closing the stream 105 | { 106 | delete d_in; 107 | d_in = 0; // switchStreams also closes 108 | } 109 | 110 | private: 111 | size_t next(); // obtain the next character 112 | }; 113 | 114 | protected: 115 | struct StreamStruct 116 | { 117 | std::string pushedName; 118 | Input pushedInput; 119 | }; 120 | 121 | private: 122 | std::vector d_streamStack; 123 | 124 | std::string d_filename; // name of the currently processed 125 | static size_t s_istreamNr; // file. With istreams it receives 126 | // the name "", where 127 | // # is the sequence number of the 128 | // istream (starting at 1) 129 | int d_startCondition = 0; 130 | int d_lopSC = 0; 131 | 132 | size_t d_state = 0; 133 | int d_nextState; 134 | std::shared_ptr d_out; 135 | bool d_atBOL = true; // the matched text starts at BOL 136 | Final d_final; 137 | 138 | // only used interactively: 139 | std::istream *d_in; // points to the input stream 140 | std::shared_ptr d_line; // holds line fm d_in 141 | 142 | Input d_input; 143 | std::string d_matched; // matched characters 144 | std::string d_lopMatched; // matched lop-rule characters 145 | std::string::iterator d_lopIter; 146 | std::string::iterator d_lopTail; 147 | std::string::iterator d_lopEnd; 148 | 149 | size_t d_lopPending; // # pending input chars at lop1_ 150 | bool d_return; // return after a rule's action 151 | bool d_more = false; // set to true by more() 152 | 153 | size_t (ScannerBase::*d_get)() = &ScannerBase::getInput; 154 | 155 | protected: 156 | std::istream *d_in_; 157 | int d_token_; // returned by lex_ 158 | 159 | 160 | 161 | int const (*d_dfaBase_)[68]; 162 | 163 | static int const s_dfa_[][68]; 164 | static int const (*s_dfaBase_[])[68]; 165 | enum: bool { s_interactive_ = false }; 166 | enum: size_t { 167 | s_rangeOfEOF_ = 65, 168 | s_finIdx_ = 66, 169 | s_nRules_ = 71, 170 | s_maxSizeofStreamStack_ = 10 171 | }; 172 | static size_t const s_ranges_[]; 173 | static size_t const s_rf_[][2]; 174 | 175 | public: 176 | ScannerBase(ScannerBase const &other) = delete; 177 | ScannerBase &operator=(ScannerBase const &rhs) = delete; 178 | 179 | bool debug() const; 180 | std::string const &filename() const; 181 | std::string const &matched() const; 182 | 183 | size_t length() const; 184 | size_t lineNr() const; 185 | 186 | void setDebug(bool onOff); 187 | 188 | void switchOstream(std::ostream &out); 189 | void switchOstream(std::string const &outfilename); 190 | 191 | 192 | void switchStreams(std::istream &in, 193 | std::ostream &out = std::cout); 194 | 195 | void switchIstream(std::string const &infilename); 196 | void switchStreams(std::string const &infilename, 197 | std::string const &outfilename); 198 | 199 | 200 | // $insert interactiveDecl 201 | 202 | protected: 203 | ScannerBase(std::istream &in, std::ostream &out); 204 | ScannerBase(std::string const &infilename, std::string const &outfilename); 205 | ~ScannerBase(); 206 | 207 | bool popStream(); 208 | std::ostream &out(); 209 | void echo() const; 210 | void leave(int retValue) const; 211 | 212 | // `accept(n)' returns all but the first `n' characters of the current 213 | // token back to the input stream, where they will be rescanned when the 214 | // scanner looks for the next match. 215 | // So, it matches n of the characters in the input buffer, and so it accepts 216 | // n characters, rescanning the rest. 217 | void accept(size_t nChars = 0); // former: less 218 | void redo(size_t nChars = 0); // rescan the last nChar 219 | // characters, reducing 220 | // length() by nChars 221 | void more(); 222 | void push(size_t ch); // push char to Input 223 | void push(std::string const &txt); // same: chars 224 | 225 | 226 | std::vector const &streamStack() const; 227 | 228 | void pushStream(std::istream &curStream); 229 | void pushStream(std::string const &curName); 230 | 231 | 232 | void setFilename(std::string const &name); 233 | void setMatched(std::string const &text); 234 | 235 | static std::string istreamName_(); 236 | 237 | // members used by lex_(): they end in _ and should not be used 238 | // otherwise. 239 | 240 | ActionType_ actionType_(size_t range); // next action 241 | bool return_(); // 'return' from codeblock 242 | size_t matched_(size_t ch); // handles a matched rule 243 | size_t getRange_(int ch); // convert char to range 244 | size_t get_(); // next character 245 | size_t state_() const; // current state 246 | void continue_(int ch); // handles a transition 247 | void echoCh_(size_t ch); // echoes ch, sets d_atBOL 248 | void echoFirst_(size_t ch); // handles unknown input 249 | void updateFinals_(); // update a state's Final info 250 | void noReturn_(); // d_return to false 251 | void print_() const; // optionally print token 252 | void pushFront_(size_t ch); // return char to Input 253 | void reset_(); // prepare for new cycle 254 | // next input stream: 255 | void switchStream_(std::istream &in, size_t lineNr); 256 | void lopf_(size_t tail); // matched fixed size tail 257 | void lop1_(int lopSC); // matched ab for a/b 258 | void lop2_(); // matches the LOP's b tail 259 | void lop3_(); // catch-all while matching b 260 | void lop4_(); // matches the LOP's a head 261 | 262 | // $insert startconddecl 263 | StartCondition_ startCondition() const; // current start condition 264 | void begin(StartCondition_ startCondition); 265 | 266 | private: 267 | static StartCondition_ constexpr SC(int sc); 268 | static int constexpr SC(StartCondition_ sc); 269 | 270 | size_t getInput(); 271 | size_t getLOP(); 272 | void p_pushStream(std::string const &name, std::istream *streamPtr); 273 | void setMatchedSize(size_t length); 274 | bool knownFinalState(); 275 | 276 | template 277 | static ReturnType constexpr as(ArgType value); 278 | static bool constexpr available(size_t value); 279 | }; 280 | 281 | inline ScannerBase::~ScannerBase() 282 | { 283 | d_input.close(); 284 | } 285 | 286 | template 287 | inline ReturnType constexpr ScannerBase::as(ArgType value) 288 | { 289 | return static_cast(value); 290 | } 291 | 292 | // $insert startcondimpl 293 | inline ScannerBase::StartCondition_ constexpr ScannerBase::SC(int sc) 294 | { 295 | return as(sc); 296 | } 297 | 298 | inline int constexpr ScannerBase::SC(StartCondition_ sc) 299 | { 300 | return as(sc); 301 | } 302 | 303 | inline ScannerBase::StartCondition_ ScannerBase::startCondition() const 304 | { 305 | return SC(d_startCondition); 306 | } 307 | 308 | inline void ScannerBase::begin(StartCondition_ startCondition) 309 | { 310 | // d_state is reset to 0 by reset_() 311 | d_dfaBase_ = s_dfaBase_[d_startCondition = SC(startCondition)]; 312 | } 313 | 314 | inline bool ScannerBase::knownFinalState() 315 | { 316 | return (d_atBOL && available(d_final.bol.rule)) || 317 | available(d_final.std.rule); 318 | } 319 | 320 | inline bool constexpr ScannerBase::available(size_t value) 321 | { 322 | return value != std::numeric_limits::max(); 323 | } 324 | 325 | inline std::ostream &ScannerBase::out() 326 | { 327 | return *d_out; 328 | } 329 | 330 | inline void ScannerBase::push(size_t ch) 331 | { 332 | d_input.reRead(ch); 333 | } 334 | 335 | inline void ScannerBase::push(std::string const &str) 336 | { 337 | d_input.reRead(str, 0); 338 | } 339 | 340 | inline std::vector const &ScannerBase::streamStack() const 341 | { 342 | return d_streamStack; 343 | } 344 | 345 | inline void ScannerBase::setFilename(std::string const &name) 346 | { 347 | d_filename = name; 348 | } 349 | 350 | inline void ScannerBase::setMatched(std::string const &text) 351 | { 352 | d_matched = text; 353 | } 354 | 355 | inline std::string const &ScannerBase::matched() const 356 | { 357 | return d_matched; 358 | } 359 | 360 | inline std::string const &ScannerBase::filename() const 361 | { 362 | return d_filename; 363 | } 364 | 365 | inline void ScannerBase::echo() const 366 | { 367 | *d_out << d_matched; 368 | } 369 | 370 | inline size_t ScannerBase::length() const 371 | { 372 | return d_matched.size(); 373 | } 374 | 375 | inline void ScannerBase::leave(int retValue) const 376 | { 377 | throw as(retValue); 378 | } 379 | 380 | inline size_t ScannerBase::lineNr() const 381 | { 382 | return d_input.lineNr(); 383 | } 384 | 385 | inline void ScannerBase::more() 386 | { 387 | d_more = true; 388 | } 389 | 390 | inline size_t ScannerBase::state_() const 391 | { 392 | return d_state; 393 | } 394 | 395 | inline size_t ScannerBase::get_() 396 | { 397 | return (this->*d_get)(); 398 | } 399 | 400 | inline size_t ScannerBase::getInput() 401 | { 402 | return d_input.get(); 403 | } 404 | 405 | inline bool ScannerBase::return_() 406 | { 407 | return d_return; 408 | } 409 | 410 | inline void ScannerBase::noReturn_() 411 | { 412 | d_return = false; 413 | } 414 | 415 | 416 | #endif // ScannerBASE_H_INCLUDED 417 | -------------------------------------------------------------------------------- /src/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILER_H 2 | #define COMPILER_H 3 | 4 | #include "compilerbase.h" 5 | #undef Compiler 6 | // CAVEAT: between the baseclass-include directive and the 7 | // #undef directive in the previous line references to Compiler 8 | // are read as CompilerBase. 9 | // If you need to include additional headers in this file 10 | // you should do so after these comment-lines. 11 | 12 | #include 13 | #include 14 | #include 15 | #include "scanner.h" 16 | #include "bfgenerator.h" 17 | #include "memory.h" 18 | #include "scope.h" 19 | 20 | class Compiler: public CompilerBase 21 | { 22 | public: 23 | enum class CellType 24 | { 25 | INT8, 26 | INT16, 27 | INT32 28 | }; 29 | 30 | struct Options 31 | { 32 | Compiler::CellType cellType{Compiler::CellType::INT8}; 33 | std::vector includePaths; 34 | std::string bfxFile; 35 | std::string testFile;; 36 | std::string profileFile; 37 | std::ostream* outStream{&std::cout}; 38 | bool constEvalAllowed{true}; 39 | bool randomEnabled{false}; 40 | bool bcrEnabled{true}; 41 | bool includeWarningEnabled{true}; 42 | bool assertWarningEnabled{true}; 43 | int maxUnrollIterations{20}; 44 | }; 45 | 46 | private: 47 | static constexpr int TAPE_SIZE_INITIAL{30000}; 48 | 49 | long const MAX_INT; 50 | long const MAX_ARRAY_SIZE; 51 | int const MAX_LOOP_UNROLL_ITERATIONS{20}; 52 | 53 | std::string const d_sourceFile; 54 | CellType const d_cellType; 55 | 56 | Scanner d_scanner; 57 | Memory d_memory; 58 | Scope d_scope; 59 | BFGenerator d_bfGen; 60 | 61 | std::map d_functionMap; 62 | std::map d_constMap; 63 | std::vector d_includePaths; 64 | std::vector d_included; 65 | std::ostringstream d_codeBuffer; 66 | 67 | using BcrMapType = std::map>; 68 | BcrMapType d_bcrMap; 69 | 70 | enum class Stage 71 | { 72 | IDLE, 73 | PARSING, 74 | CODEGEN, 75 | FINISHED 76 | }; 77 | 78 | Stage d_stage{Stage::IDLE}; 79 | std::string d_instructionFilename; 80 | int d_instructionLineNr; 81 | bool d_constEvalEnabled{true}; 82 | bool const d_constEvalAllowed{true}; 83 | bool const d_randomExtensionEnabled{false}; 84 | int d_loopUnrolling{0}; 85 | bool d_boundsCheckingEnabled{true}; 86 | bool const d_bcrEnabled{true}; 87 | bool const d_includeWarningEnabled{true}; 88 | bool const d_assertWarningEnabled{true}; 89 | std::ostream& d_outStream; 90 | std::string const d_profileFile; 91 | 92 | std::string const d_testFile; 93 | std::vector d_testVector; 94 | std::ofstream d_testStream; 95 | 96 | struct State 97 | { 98 | Memory memory; 99 | Scope scope; 100 | BFGenerator bfGen; 101 | std::string buffer; 102 | bool constEval; 103 | int loopUnrolling; 104 | bool boundsChecking; 105 | BcrMapType bcrMap; 106 | }; 107 | 108 | enum class SubScopeType 109 | { 110 | FOR, 111 | WHILE, 112 | SWITCH, 113 | IF, 114 | ANONYMOUS 115 | }; 116 | 117 | public: 118 | 119 | Compiler(Options const &opt); 120 | int compile(); 121 | void write(); 122 | 123 | private: 124 | int parse(); 125 | void writeProfile() const; 126 | void pushStream(std::string const &file); 127 | std::string fileWithoutPath(std::string const &file); 128 | void addFunction(BFXFunction const &bfxFunc); 129 | void addGlobals(std::vector> const &declarations); 130 | void addConstant(std::string const &ident, int const num); 131 | void addStruct(std::string const &name, 132 | std::vector> const &fields); 133 | 134 | void addTest(std::string const &testName, 135 | std::vector> const &testBody); 136 | void writeTestList(); 137 | 138 | int compileTimeConstant(std::string const &ident) const; 139 | bool isCompileTimeConstant(std::string const &ident) const; 140 | 141 | State save(); 142 | void restore(State &&state); 143 | void enterScope(Scope::Type const type); 144 | void enterScope(std::string const &name); 145 | void exitScope(std::string const &name = ""); 146 | void allocateBCRFlags(bool const alloc); 147 | int getCurrentContinueFlag() const; 148 | int getCurrentBreakFlag() const; 149 | void resetContinueFlag(); 150 | 151 | 152 | bool setConstEval(bool const val); 153 | bool enableConstEval(); 154 | bool disableConstEval(); 155 | void disableBoundChecking(); 156 | void enableBoundChecking(); 157 | void sync(int const addr); 158 | int wrapValue(int val); 159 | void constEvalSetToValue(int const addr, int const val); 160 | void runtimeSetToValue(int const addr, int const val); 161 | void runtimeAssign(int const lhs, int const rhs); 162 | 163 | static bool validateFunction(BFXFunction const &bfxFunc); 164 | static std::string cancelOppositeCommands(std::string const &bf); 165 | 166 | // Memory management uitilities 167 | int allocate(std::string const &ident, TypeSystem::Type type); 168 | int allocateTemp(TypeSystem::Type type); 169 | int allocateTemp(int const sz = 1); 170 | int allocateTempBlock(int const sz); 171 | int addressOf(std::string const &ident); 172 | int staticAssert(Instruction const &check, std::string const &msg); 173 | 174 | // Instruction generator 175 | template 176 | Instruction instruction(Args ... args){ 177 | std::string file = d_scanner.filename(); 178 | int line = d_scanner.lineNr(); 179 | return Instruction([=, this](){ 180 | setFilename(file); 181 | setLineNr(line); 182 | return (this->*Member)(args ...); 183 | }); 184 | } 185 | 186 | // Wrappers for element-modifying instructions 187 | using UnaryFunction = int (Compiler::*)(AddressOrInstruction const &); 188 | int applyUnaryFunctionToElement(AddressOrInstruction const &arr, 189 | AddressOrInstruction const &index, 190 | UnaryFunction func); 191 | 192 | using BinaryFunction = int (Compiler::*)(AddressOrInstruction const &, AddressOrInstruction const &); 193 | int applyBinaryFunctionToElement(AddressOrInstruction const &arr, 194 | AddressOrInstruction const &index, 195 | AddressOrInstruction const &rhs, 196 | BinaryFunction func); 197 | 198 | // Instructions 199 | int sizeOfOperator(std::string const &ident); 200 | int constVal(int const val); 201 | int statement(Instruction const &instr); 202 | int mergeInstructions(Instruction const &instr1, Instruction const &instr2); 203 | int arrayFromSize(int const sz, Instruction const &fill); 204 | int arrayFromList(std::vector const &list); 205 | int arrayFromString(std::string const &str); 206 | int anonymousStructObject(std::string const name, std::vector const &values); 207 | int call(std::string const &functionName, std::vector const &args = {}); 208 | int declareVariable(std::string const &ident, TypeSystem::Type type); 209 | int initializeExpression(std::string const &ident, TypeSystem::Type type, 210 | AddressOrInstruction const &rhs); 211 | int assign(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 212 | int fetch(std::string const &ident); 213 | int fetchField(std::vector const &expr); 214 | int fetchFieldImpl(std::vector const &expr, int const baseAddr, size_t const baseIdx); 215 | 216 | int fetchElement(AddressOrInstruction const &arr, AddressOrInstruction const &index); 217 | int assignElement(AddressOrInstruction const &arr, AddressOrInstruction const &index, AddressOrInstruction const &rhs); 218 | int scanCell(); 219 | int randomCell(); 220 | int printCell(AddressOrInstruction const &target); 221 | int preIncrement(AddressOrInstruction const &addr); 222 | int preDecrement(AddressOrInstruction const &addr); 223 | int postIncrement(AddressOrInstruction const &addr); 224 | int postDecrement(AddressOrInstruction const &addr); 225 | int addTo(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 226 | int add(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 227 | int subtractFrom(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 228 | int subtract(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 229 | int multiplyBy(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 230 | int multiply(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 231 | int power(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 232 | int powerBy(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 233 | int equal(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 234 | int notEqual(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 235 | int less(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 236 | int greater(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 237 | int lessOrEqual(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 238 | int greaterOrEqual(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 239 | int logicalNot(AddressOrInstruction const &arg); 240 | int logicalAnd(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 241 | int logicalOr(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 242 | int divideBy(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 243 | int divide(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 244 | int moduloBy(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 245 | int modulo(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 246 | int divMod(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 247 | int modDiv(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs); 248 | void divModPair(AddressOrInstruction const &lhs, AddressOrInstruction const &rhs, 249 | int const divResult, int const modResult); 250 | 251 | int ifStatement(Instruction const &condition, Instruction const &ifBody, Instruction const &elseBody, bool const scoped = true); 252 | int forStatement(Instruction const &init, Instruction const &condition, 253 | Instruction const &increment, Instruction const &body); 254 | int forStatementRuntime(Instruction const &init, Instruction const &condition, 255 | Instruction const &increment, Instruction const &body); 256 | int forRangeStatement(BFXFunction::Parameter const ¶m, Instruction const &array, Instruction const &body); 257 | int forRangeStatementRuntime(BFXFunction::Parameter const ¶m, Instruction const &array, Instruction const &body); 258 | 259 | int whileStatement(Instruction const &condition, Instruction const &body); 260 | int whileStatementRuntime(Instruction const &condition, Instruction const &body); 261 | 262 | int switchStatement(Instruction const &compareExpr, 263 | std::vector> const &cases, 264 | Instruction const &defaultCase); 265 | int breakStatement(); 266 | int continueStatement(); 267 | int returnStatement(); 268 | 269 | 270 | // Constant evaluation 271 | template 272 | int eval(BFGenFunc&& bfFunc, ConstFunc&& constFunc, int const resultAddr, Args&& ... args); 273 | 274 | // Error handling (implementations below) 275 | template 276 | void compilerError(First const &first, Rest&& ... rest) const; 277 | 278 | template 279 | void compilerWarning(First const &first, Rest&& ... rest) const; 280 | 281 | template 282 | void compilerErrorIf(bool const condition, First const &first, Rest&& ... rest) const; 283 | 284 | template 285 | void compilerWarningIf(bool const condition, First const &first, Rest&& ... rest) const; 286 | 287 | template 288 | void validateAddr__(std::string const &function, int first, Rest&& ... rest) const; 289 | 290 | void setFilename(std::string const &file); 291 | void setLineNr(int const line); 292 | int lineNr() const; 293 | std::string filename() const; 294 | 295 | 296 | // BisonC++ generated 297 | void error(); // called on (syntax) errors 298 | int lex(); 299 | void print(); 300 | 301 | void exceptionHandler(std::exception const &exc); 302 | 303 | // support functions for parse(): 304 | void executeAction_(int ruleNr); 305 | void errorRecovery_(); 306 | void nextCycle_(); 307 | void nextToken_(); 308 | void print_(); 309 | }; 310 | 311 | 312 | // Implementation of the errorIf<> and validateAddr__<> function templates 313 | template 314 | void Compiler::compilerWarning(First const &first, Rest&& ... rest) const 315 | { 316 | std::cerr << "Warning: in " << filename() << " on line " << lineNr() << ": " << first; 317 | (std::cerr << ... << rest) << '\n'; 318 | } 319 | 320 | template 321 | void Compiler::compilerError(First const &first, Rest&& ... rest) const 322 | { 323 | std::cerr << "Error in " << filename() << " on line " << lineNr() << ": " << first; 324 | (std::cerr << ... << rest) << '\n'; 325 | 326 | try { 327 | CompilerBase::ERROR(); 328 | } 329 | catch (...) 330 | { 331 | std::cerr << "Unable to recover: compilation terminated.\n"; 332 | std::exit(1); 333 | } 334 | } 335 | 336 | template 337 | void Compiler::compilerErrorIf(bool const condition, First const &first, Rest&& ... rest) const 338 | { 339 | if (condition) 340 | compilerError(first, std::forward(rest)...); 341 | } 342 | 343 | template 344 | void Compiler::compilerWarningIf(bool const condition, First const &first, Rest&& ... rest) const 345 | { 346 | if (condition) 347 | compilerWarning(first, std::forward(rest)...); 348 | } 349 | 350 | template 351 | int Compiler::eval(BFGenFunc&& bfFunc, ConstFunc&& constFunc, int const resultAddr, Args&& ... args) 352 | { 353 | static_assert((std::is_convertible_v && ...), 354 | "Operands to constEval must be (convertible) to int (addresses)."); 355 | 356 | static constexpr int N = (sizeof ... (Args)); 357 | int const arguments[N] = {args ...}; 358 | constexpr auto isVolatile = 359 | [](int const argIdx) 360 | { 361 | return VolatileMask & (1 << (N - argIdx - 1)); 362 | }; 363 | 364 | bool const canBeConstEvaluated = (d_memory.valueKnown(args) && ...); 365 | if (canBeConstEvaluated && d_constEvalEnabled) 366 | { 367 | // Evaluate using constfunc 368 | constEvalSetToValue(resultAddr, constFunc(d_memory.value(args) ...)); 369 | 370 | // Application of constFunc may have resulted in side-effects if it accepted 371 | // reference-parameters. Check Mask for volatile values -> 372 | // for each changed value, set its sync-flag to false. 373 | 374 | for (int i = 0; i != N; ++i) 375 | if (isVolatile(i)) 376 | d_memory.setSync(arguments[i], false); 377 | } 378 | else 379 | { 380 | if (d_constEvalEnabled) 381 | { 382 | for (int i = 0; i != N; ++i) 383 | sync(arguments[i]); 384 | } 385 | 386 | bfFunc(); 387 | 388 | // Runtime evaluation has resulted in now unknown values. The mask indicates which 389 | // addresses may have changed during runtime. The return-address will always be 390 | // set as unknown. 391 | d_memory.setValueUnknown(resultAddr); 392 | for (int i = 0; i != N; ++i) 393 | if (isVolatile(i)) 394 | d_memory.setValueUnknown(arguments[i]); 395 | } 396 | 397 | return resultAddr; 398 | } 399 | 400 | inline std::ostream &operator<<(std::ostream &out, Compiler::CellType type) 401 | { 402 | switch (type) 403 | { 404 | case Compiler::CellType::INT8: return (out << "int8"); 405 | case Compiler::CellType::INT16: return (out << "int16"); 406 | case Compiler::CellType::INT32: return (out << "int32"); 407 | } 408 | 409 | assert(false && "unreachable"); 410 | } 411 | 412 | #endif //COMPILER_H 413 | -------------------------------------------------------------------------------- /src/interpreter/bfint.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef USE_CURSES 8 | #include 9 | #endif 10 | 11 | #include "bfint.h" 12 | 13 | namespace _MaxInt 14 | { 15 | template 16 | constexpr size_t _getMax() 17 | { 18 | return (static_cast(1) << (8 * sizeof(T))) - 1; 19 | } 20 | 21 | inline static size_t get(CellType c) 22 | { 23 | switch (c) 24 | { 25 | case CellType::INT8: return _getMax(); 26 | case CellType::INT16: return _getMax(); 27 | case CellType::INT32: return _getMax(); 28 | } 29 | throw -1; 30 | } 31 | } 32 | 33 | BFInterpreter::BFInterpreter(Options const &opt): 34 | d_array(opt.tapeLength), 35 | d_uniformDist(0, (opt.randMax != 0) ? opt.randMax : _MaxInt::get(opt.cellType)), 36 | d_cellType(opt.cellType), 37 | d_randomEnabled(opt.randomEnabled), 38 | d_randMax(opt.randMax), 39 | d_randomWarningEnabled(opt.randomWarningEnabled), 40 | d_gamingMode(opt.gamingMode), 41 | d_testFile(opt.testFile) 42 | { 43 | // init code 44 | std::ifstream file(opt.bfFile); 45 | if (!file.is_open()) 46 | throw std::string("File not found: ") + opt.bfFile; 47 | 48 | std::stringstream buffer; 49 | buffer << file.rdbuf(); 50 | d_code = buffer.str(); 51 | 52 | // init rng 53 | auto t0 = std::chrono::system_clock::now().time_since_epoch(); 54 | auto ms = duration_cast(t0).count(); 55 | d_rng.seed(ms); 56 | } 57 | 58 | void BFInterpreter::reset() 59 | { 60 | std::fill(d_array.begin(), d_array.end(), 0); 61 | d_arrayPointer = 0; 62 | d_codePointer = 0; 63 | d_loopStack = std::stack{}; 64 | } 65 | 66 | int BFInterpreter::run() 67 | { 68 | if (d_testFile.empty()) 69 | return run(std::cin, std::cout); 70 | 71 | auto const report = 72 | [](std::string const &testName, std::string const &caseName, 73 | std::string const &output, std::string const &expect) 74 | { 75 | std::cout << '<' << testName << "::" << caseName << "> "; 76 | if (output == expect) 77 | { 78 | std::cout << "PASS\n"; 79 | return 0; 80 | } 81 | 82 | std::cout << "FAIL\n" 83 | << "\tExpected: \""; 84 | for (char c: expect) 85 | std::cout << (std::isprint(c) ? c : '.'); 86 | std::cout << "\"\n\tGot: \""; 87 | for (char c: output) 88 | std::cout << (std::isprint(c) ? c : '.'); 89 | std::cout << "\"\n"; 90 | return 1; 91 | }; 92 | 93 | auto const split = [](std::string const &str, char const s) 94 | { 95 | std::vector result; 96 | std::string current; 97 | for (char c: str) 98 | { 99 | if (c == s) 100 | { 101 | result.push_back(current); 102 | current = ""; 103 | } 104 | else 105 | current += c; 106 | } 107 | result.push_back(current); 108 | return result; 109 | }; 110 | 111 | auto const loadStringStream = 112 | [](std::stringstream &ss, std::string const &filename) 113 | { 114 | std::ifstream file(filename); 115 | if (!file) 116 | { 117 | std::cerr << "ERROR: coult not open file " << filename << '\n'; 118 | return false; 119 | } 120 | ss << file.rdbuf(); 121 | return true; 122 | }; 123 | 124 | 125 | std::ifstream file(d_testFile); 126 | if (!file) 127 | { 128 | std::cerr << "ERROR: coult not open test-file " << d_testFile << '\n'; 129 | return -1; 130 | } 131 | 132 | std::vector lines; 133 | std::string line; 134 | while (std::getline(file, line)) 135 | lines.push_back(line); 136 | 137 | int errCount = 0; 138 | for (std::string const &base: lines) 139 | { 140 | std::vector parts = split(base, '-'); 141 | assert(parts.size() == 3); 142 | 143 | std::string const testName = parts[1]; 144 | std::string const caseName = parts[2]; 145 | 146 | std::stringstream inputString; 147 | if (!loadStringStream(inputString, base + ".input")) 148 | return -1; 149 | 150 | std::stringstream expectString; 151 | if (!loadStringStream(expectString, base + ".expect")) 152 | return -1; 153 | 154 | std::ostringstream bfOutput; 155 | run(inputString, bfOutput); 156 | errCount += report(testName, caseName, bfOutput.str(), expectString.str()); 157 | } 158 | 159 | return errCount; 160 | } 161 | 162 | int BFInterpreter::run(std::istream &in, std::ostream &out) 163 | { 164 | reset(); 165 | 166 | #ifdef USE_CURSES 167 | // Setup ncurses window 168 | if (d_gamingMode) 169 | { 170 | auto win = initscr(); 171 | scrollok(win, true); 172 | cbreak(); 173 | noecho(); 174 | nonl(); 175 | nodelay(stdscr, TRUE); 176 | curs_set(0); 177 | 178 | signal(SIGINT, [](int sig){ 179 | finish(sig); 180 | }); 181 | } 182 | #endif 183 | 184 | while (true) 185 | { 186 | char token = d_code[d_codePointer]; 187 | switch (token) 188 | { 189 | case LEFT: pointerDec(); break; 190 | case RIGHT: pointerInc(); break; 191 | case PLUS: plus(); break; 192 | case MINUS: minus(); break; 193 | case PRINT: 194 | { 195 | if (d_gamingMode) 196 | printCurses(); 197 | else 198 | print(out); 199 | break; 200 | } 201 | case READ: 202 | { 203 | if (d_gamingMode) 204 | readCurses(); 205 | else 206 | read(in); 207 | break; 208 | } 209 | case START_LOOP: startLoop(); break; 210 | case END_LOOP: endLoop(); break; 211 | case RAND: 212 | { 213 | static bool warned = false; 214 | if (d_randomEnabled) 215 | random(); 216 | else if (d_randomWarningEnabled && !warned) 217 | { 218 | static std::string const warning = 219 | "\n" 220 | "=========================== !!!!!! ==============================\n" 221 | "Warning: BF-code contains '?'-commands, which may be\n" 222 | "interpreted as the random-operation, an extension to the\n" 223 | "canonical BF instructionset. This extension can be enabled\n" 224 | "with the --random option.\n" 225 | "This warning can be disabled with the --no-random-warning option.\n" 226 | "=========================== !!!!!! ==============================\n"; 227 | 228 | if (!d_gamingMode) 229 | std::cerr << warning; 230 | else 231 | { 232 | #ifdef USE_CURSES 233 | addstr(warning.c_str()); 234 | #else 235 | assert(false); 236 | #endif 237 | } 238 | warned = true; 239 | } 240 | break; 241 | } 242 | default: break; 243 | } 244 | 245 | if (++d_codePointer >= d_code.size()) 246 | break; 247 | } 248 | 249 | #ifdef USE_CURSES 250 | if (d_gamingMode) 251 | { 252 | nodelay(stdscr, false); 253 | getch(); 254 | finish(0); 255 | } 256 | #endif 257 | 258 | return 0; 259 | } 260 | 261 | int BFInterpreter::consume(Ops op) 262 | { 263 | assert(d_code[d_codePointer] == op && "codepointer should be pointing at op now"); 264 | 265 | int n = 1; 266 | while (d_code[d_codePointer + n] == op) 267 | ++n; 268 | 269 | d_codePointer += (n - 1); 270 | return n; 271 | } 272 | 273 | void BFInterpreter::plus() 274 | { 275 | int const n = consume(PLUS); 276 | switch (d_cellType) 277 | { 278 | case CellType::INT8: 279 | d_array[d_arrayPointer] = static_cast(d_array[d_arrayPointer] + n); 280 | break; 281 | case CellType::INT16: 282 | d_array[d_arrayPointer] = static_cast(d_array[d_arrayPointer] + n); 283 | break; 284 | case CellType::INT32: 285 | d_array[d_arrayPointer] = static_cast(d_array[d_arrayPointer] + n); 286 | break; 287 | } 288 | } 289 | 290 | void BFInterpreter::minus() 291 | { 292 | int const n = consume(MINUS); 293 | switch (d_cellType) 294 | { 295 | case CellType::INT8: 296 | d_array[d_arrayPointer] = static_cast(d_array[d_arrayPointer] - n); 297 | break; 298 | case CellType::INT16: 299 | d_array[d_arrayPointer] = static_cast(d_array[d_arrayPointer] - n); 300 | break; 301 | case CellType::INT32: 302 | d_array[d_arrayPointer] = static_cast(d_array[d_arrayPointer] - n); 303 | break; 304 | } 305 | } 306 | 307 | void BFInterpreter::pointerInc() 308 | { 309 | int const n = consume(RIGHT); 310 | d_arrayPointer += n; 311 | 312 | while (d_arrayPointer >= d_array.size()) 313 | d_array.resize(2 * d_array.size()); 314 | } 315 | 316 | void BFInterpreter::pointerDec() 317 | { 318 | if (d_arrayPointer == 0) 319 | throw std::string("Error: trying to decrement pointer beyond beginning."); 320 | 321 | int const n = consume(LEFT); 322 | d_arrayPointer -= n; 323 | } 324 | 325 | void BFInterpreter::startLoop() 326 | { 327 | if (d_array[d_arrayPointer] != 0) 328 | { 329 | d_loopStack.push(d_codePointer); 330 | } 331 | else 332 | { 333 | int bracketCount = 1; 334 | while (bracketCount != 0 && d_codePointer < d_code.size()) 335 | { 336 | ++d_codePointer; 337 | if (d_code[d_codePointer] == START_LOOP) 338 | ++bracketCount; 339 | else if (d_code[d_codePointer] == END_LOOP) 340 | --bracketCount; 341 | } 342 | } 343 | } 344 | 345 | void BFInterpreter::endLoop() 346 | { 347 | if (d_array[d_arrayPointer] != 0) 348 | { 349 | d_codePointer = d_loopStack.top(); 350 | } 351 | else 352 | { 353 | d_loopStack.pop(); 354 | } 355 | } 356 | 357 | void BFInterpreter::print(std::ostream &out) 358 | { 359 | out << (char)d_array[d_arrayPointer] << std::flush; 360 | } 361 | 362 | void BFInterpreter::printCurses() 363 | { 364 | #ifdef USE_CURSES 365 | static char const ESC = 27; // Control char 366 | static std::string ansiBuffer; 367 | 368 | char const c = d_array[d_arrayPointer]; 369 | if (c == ESC) 370 | { 371 | if (ansiBuffer.empty()) 372 | { 373 | ansiBuffer.push_back(c); 374 | } 375 | else 376 | { 377 | handleAnsi(ansiBuffer, true); 378 | ansiBuffer.push_back(c); 379 | } 380 | } 381 | else 382 | { 383 | if (ansiBuffer.empty()) 384 | { 385 | addch(c); 386 | } 387 | else 388 | { 389 | ansiBuffer.push_back(c); 390 | handleAnsi(ansiBuffer, false); 391 | } 392 | } 393 | 394 | refresh(); 395 | #else 396 | assert(false && "printCurses() called but not compiled with USE_CURSES"); 397 | #endif 398 | } 399 | 400 | void BFInterpreter::handleAnsi(std::string &ansiStr, bool const force) 401 | { 402 | #ifdef USE_CURSES 403 | static char const ESC = 27; // Control char 404 | assert(ansiStr.length() > 1 && "handleAnsi called with less than 2 characters"); 405 | assert(ansiStr[0] == ESC && "handleAnsi called on string not starting with ESC"); 406 | 407 | auto const flush = [&]() 408 | { 409 | addstr(ansiStr.c_str()); 410 | ansiStr.clear(); 411 | }; 412 | 413 | if (ansiStr.length() == 2 && ansiStr[1] != '[') 414 | { 415 | // ESC not followed by '[' -> not ansi 416 | flush(); 417 | return; 418 | } 419 | 420 | if (ansiStr.length() < 3) 421 | { 422 | if (force) 423 | flush(); 424 | 425 | return; // cannot be a complete ansi escape sequence 426 | } 427 | 428 | // 3 or more characters present 429 | int row, col; 430 | getyx(stdscr, row, col); 431 | 432 | bool handled = true; 433 | switch (ansiStr.back()) 434 | { 435 | case 'A': // cursor up 436 | { 437 | int n = std::stoi(ansiStr.substr(2, ansiStr.length() - 3)); 438 | if (row) 439 | { 440 | row = std::max(0, row - n); 441 | move(row, col); 442 | } 443 | break; 444 | } 445 | case 'B': // cursor down 446 | { 447 | int n = std::stoi(ansiStr.substr(2, ansiStr.length() - 3)); 448 | move(row + n, col); 449 | break; 450 | } 451 | case 'C': // cursor right 452 | { 453 | int n = std::stoi(ansiStr.substr(2, ansiStr.length() - 3)); 454 | move(row, col + n); 455 | break; 456 | } 457 | case 'D': // cursor left 458 | { 459 | int n = std::stoi(ansiStr.substr(2, ansiStr.length() - 3)); 460 | if (col) 461 | { 462 | col = std::max(0, col - n); 463 | move(row, col); 464 | clrtoeol(); 465 | } 466 | break; 467 | } 468 | case 'H': // cursor to coordinate 469 | { 470 | if (ansiStr.length() == 3) 471 | { 472 | move(0, 0); 473 | break; 474 | } 475 | 476 | size_t const separator = ansiStr.find(';'); 477 | if (separator == std::string::npos) 478 | { 479 | flush(); 480 | break; 481 | } 482 | 483 | row = std::stoi(ansiStr.substr(2, separator - 2)) - 1; 484 | col = std::stoi(ansiStr.substr(separator + 1, ansiStr.length() - separator - 2)) - 1; 485 | move(row, col); 486 | break; 487 | } 488 | case 'K': // clear line 489 | { 490 | int n = (ansiStr.length() == 3) ? 0 : 491 | std::stoi(ansiStr.substr(2, ansiStr.length() - 3)); 492 | 493 | switch (n) 494 | { 495 | case 0: 496 | { 497 | clrtoeol(); 498 | break; 499 | } 500 | case 1: 501 | { 502 | move(row, 0); 503 | addstr(std::string(col, ' ').c_str()); 504 | break; 505 | } 506 | case 2: 507 | { 508 | move(row, 0); 509 | clrtoeol(); 510 | move(row, col); 511 | break; 512 | } 513 | default: 514 | { 515 | handled = false; 516 | } 517 | } 518 | break; 519 | } 520 | case 'J': // clear (part of) screen 521 | { 522 | int n = (ansiStr.length() == 3) ? 0 : 523 | std::stoi(ansiStr.substr(2, ansiStr.length() - 3)); 524 | 525 | switch (n) 526 | { 527 | case 0: 528 | { 529 | clrtobot(); 530 | break; 531 | } 532 | case 1: 533 | { 534 | for (int i = 0; i <= row; ++i) 535 | { 536 | move(i, 0); 537 | clrtoeol(); 538 | } 539 | move(row, col); 540 | break; 541 | } 542 | case 2: 543 | { 544 | move(0,0); 545 | clrtobot(); 546 | break; 547 | } 548 | default: 549 | { 550 | handled = false; 551 | } 552 | } 553 | break; 554 | } 555 | default: 556 | { 557 | handled = false; 558 | } 559 | } 560 | 561 | if (handled) 562 | { 563 | ansiStr.clear(); 564 | return; 565 | } 566 | 567 | // ANSI sequence not yet terminated. Check if character is allowed 568 | if (std::string("0123456789;").find(ansiStr.back()) == std::string::npos || force) 569 | flush(); 570 | 571 | // Still going. Don't do anything and wait for next call 572 | #else 573 | assert(false && "handleAnsi called without USE_CURSES defined"); 574 | #endif 575 | } 576 | 577 | void BFInterpreter::read(std::istream &in) 578 | { 579 | char c; 580 | in.get(c); 581 | d_array[d_arrayPointer] = c; 582 | } 583 | 584 | void BFInterpreter::readCurses() 585 | { 586 | #ifdef USE_CURSES 587 | int c = getch(); 588 | d_array[d_arrayPointer] = (c < 0) ? 0 : static_cast(c); 589 | #else 590 | assert(false && "readCurses() called but not compiled with USE_CURSES"); 591 | #endif 592 | } 593 | 594 | void BFInterpreter::random() 595 | { 596 | auto val = d_uniformDist(d_rng); 597 | d_array[d_arrayPointer] = val; 598 | } 599 | 600 | void BFInterpreter::printState() 601 | { 602 | for (auto x: d_array) 603 | std::cout << (int)x << ' '; 604 | std::cout << '\n'; 605 | } 606 | 607 | void BFInterpreter::finish(int sig) 608 | { 609 | #ifdef USE_CURSES 610 | endwin(); 611 | if (sig == SIGINT) 612 | exit(0); 613 | #else 614 | assert(false && "finish() called but not compiled with USE_CURSES"); 615 | #endif 616 | } 617 | 618 | -------------------------------------------------------------------------------- /src/bfgenerator.cc: -------------------------------------------------------------------------------- 1 | #include "bfgenerator.ih" 2 | 3 | std::string BFGenerator::setToValue(int const addr, int const val) 4 | { 5 | validateAddr(addr); 6 | 7 | std::ostringstream ops; 8 | ops << movePtr(addr) // go to address 9 | << "[-]" // reset cell to 0 10 | << std::string(val, '+'); // increment to value 11 | 12 | return ops.str(); 13 | } 14 | 15 | std::string BFGenerator::setToValuePlus(int const addr, int const val) 16 | { 17 | validateAddr(addr); 18 | 19 | std::ostringstream ops; 20 | ops << movePtr(addr) // go to address 21 | << "[+]" // reset cell to 0 22 | << std::string(val, '+'); // increment to value 23 | 24 | return ops.str(); 25 | } 26 | 27 | std::string BFGenerator::setToValue(int const start, int const val, size_t const n) 28 | { 29 | validateAddr(start); 30 | 31 | std::ostringstream ops; 32 | for (size_t i = 0; i != n; ++i) 33 | ops << setToValue(start + i, val); 34 | 35 | return ops.str(); 36 | } 37 | 38 | std::string BFGenerator::setToValuePlus(int const addr, int const val, size_t const n) 39 | { 40 | validateAddr(addr); 41 | 42 | std::ostringstream ops; 43 | for (size_t i = 0; i != n; ++i) 44 | ops << setToValuePlus(addr + i, val); 45 | 46 | return ops.str(); 47 | } 48 | std::string BFGenerator::scan(int const addr) 49 | { 50 | std::ostringstream ops; 51 | ops << movePtr(addr) 52 | << ','; 53 | 54 | return ops.str(); 55 | } 56 | 57 | std::string BFGenerator::print(int const addr) 58 | { 59 | std::ostringstream ops; 60 | ops << movePtr(addr) 61 | << '.'; 62 | 63 | return ops.str(); 64 | } 65 | 66 | std::string BFGenerator::random(int const addr) 67 | { 68 | std::ostringstream ops; 69 | ops << movePtr(addr) 70 | << '?'; 71 | 72 | return ops.str(); 73 | } 74 | 75 | std::string BFGenerator::assign(int const lhs, int const rhs) 76 | { 77 | validateAddr(lhs, rhs); 78 | 79 | int const tmp = f_getTemp(); 80 | 81 | std::ostringstream ops; 82 | ops << setToValue(lhs, 0) 83 | << setToValue(tmp, 0) 84 | 85 | // Move contents of RHS to both LHS and TMP (backup) 86 | << movePtr(rhs) 87 | << "[" 88 | << incr(lhs) 89 | << incr(tmp) 90 | << decr(rhs) 91 | << "]" 92 | 93 | // Restore RHS by moving TMP back into it 94 | << movePtr(tmp) 95 | << "[" 96 | << incr(rhs) 97 | << decr(tmp) 98 | << "]" 99 | 100 | // Leave pointer at lhs 101 | << movePtr(lhs); 102 | 103 | return ops.str(); 104 | } 105 | 106 | std::string BFGenerator::movePtr(int const addr) 107 | { 108 | validateAddr(addr); 109 | 110 | ++d_profile[addr]; 111 | int const diff = (int)addr - (int)d_pointer; 112 | d_pointer = addr; 113 | return (diff >= 0) ? std::string(diff, '>') : std::string(-diff, '<'); 114 | } 115 | 116 | std::string BFGenerator::addConst(int const target, int const amount) 117 | { 118 | validateAddr(target); 119 | 120 | std::ostringstream ops; 121 | ops << movePtr(target) 122 | << ((amount >= 0) ? std::string(amount, '+') : std::string(-amount, '-')); 123 | 124 | return ops.str(); 125 | } 126 | 127 | std::string BFGenerator::addTo(int const target, int const rhs) 128 | { 129 | validateAddr(target, rhs); 130 | 131 | std::ostringstream ops; 132 | int const tmp = f_getTemp(); 133 | ops << assign(tmp, rhs) 134 | << "[" 135 | << incr(target) 136 | << decr(tmp) 137 | << "]" 138 | << movePtr(target); 139 | 140 | return ops.str(); 141 | } 142 | 143 | std::string BFGenerator::subtractFrom(int const target, int const rhs) 144 | { 145 | validateAddr(target, rhs); 146 | 147 | int const tmp = f_getTemp(); 148 | std::ostringstream ops; 149 | ops << assign(tmp, rhs) 150 | << "[" 151 | << decr(target) 152 | << decr(tmp) 153 | << "]" 154 | << movePtr(target); 155 | 156 | return ops.str(); 157 | } 158 | 159 | std::string BFGenerator::incr(int const target) 160 | { 161 | validateAddr(target); 162 | return movePtr(target) + "+"; 163 | } 164 | 165 | std::string BFGenerator::decr(int const target) 166 | { 167 | validateAddr(target); 168 | return movePtr(target) + "-"; 169 | } 170 | 171 | std::string BFGenerator::safeDecr(int const target, int const underflowFlag) 172 | { 173 | validateAddr(target, underflowFlag); 174 | 175 | std::ostringstream ops; 176 | ops << logicalNot(target, underflowFlag) 177 | << movePtr(target) 178 | << "-"; 179 | 180 | return ops.str(); 181 | } 182 | 183 | std::string BFGenerator::multiply(int const lhs, int const rhs, int const result) 184 | { 185 | validateAddr(lhs, rhs, result); 186 | 187 | std::ostringstream ops; 188 | ops << assign(result, lhs) 189 | << multiplyBy(result, rhs); 190 | 191 | return ops.str(); 192 | } 193 | 194 | std::string BFGenerator::multiplyBy(int const target, int const factor) 195 | { 196 | validateAddr(target, factor); 197 | 198 | int const tmp = f_getTempBlock(2); 199 | int const targetCopy = tmp + 0; 200 | int const count = tmp + 1; 201 | 202 | std::ostringstream ops; 203 | ops << assign(targetCopy, target) 204 | << setToValue(target, 0) 205 | << assign(count, factor) 206 | << "[" 207 | << addTo(target, targetCopy) 208 | << decr(count) 209 | << "]" 210 | << movePtr(target); 211 | 212 | return ops.str(); 213 | } 214 | 215 | std::string BFGenerator::power(int const lhs, int const rhs, int const result) 216 | { 217 | validateAddr(lhs, rhs, result); 218 | 219 | std::ostringstream ops; 220 | ops << assign(result, lhs) 221 | << powerBy(result, rhs); 222 | 223 | return ops.str(); 224 | } 225 | 226 | std::string BFGenerator::powerBy(int const base, int const pow) 227 | { 228 | validateAddr(base, pow); 229 | 230 | int const tmp = f_getTempBlock(2); 231 | int const baseCopy = tmp + 0; 232 | int const powCopy = tmp + 1; 233 | 234 | std::ostringstream ops; 235 | ops << assign(baseCopy, base) 236 | << setToValue(base, 1) 237 | << assign(powCopy, pow) 238 | << "[" 239 | << multiplyBy(base, baseCopy) 240 | << decr(powCopy) 241 | << "]" 242 | << movePtr(base); 243 | 244 | return ops.str(); 245 | } 246 | 247 | 248 | 249 | std::string BFGenerator::logicalNot(int const addr, int const result) 250 | { 251 | validateAddr(addr, result); 252 | 253 | int const tmp = f_getTemp(); 254 | std::ostringstream ops; 255 | 256 | ops << setToValue(result, 1) 257 | << assign(tmp, addr) 258 | << "[" 259 | << setToValue(result, 0) 260 | << setToValue(tmp, 0) 261 | << "]" 262 | << movePtr(result); 263 | 264 | return ops.str(); 265 | } 266 | 267 | std::string BFGenerator::logicalNot(int const addr) 268 | { 269 | validateAddr(addr); 270 | 271 | int flag = f_getTemp(); 272 | 273 | std::ostringstream ops; 274 | ops << setToValue(flag, 1) 275 | << movePtr(addr) 276 | << "[" 277 | << setToValue(flag, 0) 278 | << setToValue(addr, 0) 279 | << "]" 280 | << movePtr(flag) 281 | << "[" 282 | << setToValue(addr, 1) 283 | << setToValue(flag, 0) 284 | << "]" 285 | << movePtr(addr); 286 | 287 | return ops.str(); 288 | } 289 | 290 | std::string BFGenerator::logicalAnd(int const lhs, int const rhs, int const result) 291 | { 292 | validateAddr(lhs, rhs, result); 293 | 294 | int const tmp = f_getTempBlock(2); 295 | int const x = tmp + 0; 296 | int const y = tmp + 1; 297 | 298 | std::ostringstream ops; 299 | ops << setToValue(result, 0) 300 | << assign(y, rhs) 301 | << assign(x, lhs) 302 | << "[" 303 | << movePtr(y) 304 | << "[" 305 | << setToValue(result, 1) 306 | << setToValue(y, 0) 307 | << "]" 308 | << setToValue(x, 0) 309 | << "]" 310 | << movePtr(result); 311 | 312 | return ops.str(); 313 | } 314 | 315 | std::string BFGenerator::logicalAnd(int const lhs, int const rhs) 316 | { 317 | validateAddr(lhs, rhs); 318 | 319 | int const result = f_getTemp(); 320 | 321 | std::ostringstream ops; 322 | ops << logicalAnd(lhs, rhs, result) 323 | << assign(lhs, result); 324 | 325 | return ops.str(); 326 | } 327 | 328 | 329 | std::string BFGenerator::logicalOr(int const lhs, int const rhs, int const result) 330 | { 331 | validateAddr(lhs, rhs, result); 332 | 333 | int const tmp = f_getTempBlock(2); 334 | int const x = tmp + 0; 335 | int const y = tmp + 1; 336 | 337 | std::ostringstream ops; 338 | ops << setToValue(result, 0) 339 | << assign(x, lhs) 340 | << "[" 341 | << setToValue(result, 1) 342 | << setToValue(x, 0) 343 | << "]" 344 | << assign(y, rhs) 345 | << "[" 346 | << setToValue(result, 1) 347 | << setToValue(y, 0) 348 | << "]" 349 | << movePtr(result); 350 | 351 | return ops.str(); 352 | } 353 | 354 | std::string BFGenerator::logicalOr(int const lhs, int const rhs) 355 | { 356 | validateAddr(lhs, rhs); 357 | 358 | int const result = f_getTemp(); 359 | 360 | std::ostringstream ops; 361 | ops << logicalOr(lhs, rhs, result) 362 | << assign(lhs, result); 363 | 364 | return ops.str(); 365 | } 366 | 367 | std::string BFGenerator::equal(int const lhs, int const rhs, int const result) 368 | { 369 | validateAddr(lhs, rhs, result); 370 | 371 | int const tmp = f_getTempBlock(6); 372 | int const x = tmp + 0; 373 | int const y = tmp + 1; 374 | int const underflow1 = tmp + 2; 375 | int const underflow2 = tmp + 3; 376 | int const underflow3 = tmp + 4; 377 | int const yBigger = tmp + 5; 378 | 379 | std::ostringstream ops; 380 | ops << setToValue(result, 1) 381 | << setToValue(underflow1, 0) 382 | << assign(y, rhs) 383 | << assign(x, lhs) 384 | << "[" 385 | << safeDecr(y, underflow2) 386 | << logicalOr(underflow1, underflow2) 387 | << movePtr(underflow2) 388 | << "[" 389 | << setToValuePlus(y, 0) 390 | << setToValue(underflow2, 0) 391 | << "]" 392 | << decr(x) 393 | << "]" 394 | << assign(underflow3, underflow1) 395 | << "[" // if underflow -> y was smaller than x so not equal 396 | << setToValue(result, 0) 397 | << setToValuePlus(y, 1) 398 | << setToValue(underflow3, 0) 399 | << "]" 400 | << logicalNot(underflow1) 401 | << logicalAnd(y, underflow1, yBigger) 402 | << "[" // if y > 0 and did not underflow -> y was bigger than x so not equal 403 | << setToValue(result, 0) 404 | << setToValue(yBigger, 0) 405 | << "]" 406 | << movePtr(result); 407 | 408 | return ops.str(); 409 | } 410 | 411 | std::string BFGenerator::notEqual(int const lhs, int const rhs, int const result) 412 | { 413 | validateAddr(lhs, rhs, result); 414 | 415 | int const isEqual = f_getTemp(); 416 | std::ostringstream ops; 417 | ops << equal(lhs, rhs, isEqual) 418 | << logicalNot(isEqual, result); 419 | 420 | return ops.str(); 421 | } 422 | 423 | std::string BFGenerator::greater(int const lhs, int const rhs, int const result) 424 | { 425 | validateAddr(lhs, rhs, result); 426 | 427 | int const tmp = f_getTempBlock(3); 428 | int const x = tmp + 0; 429 | int const y = tmp + 1; 430 | int const underflow = tmp + 2; 431 | 432 | std::ostringstream ops; 433 | ops << setToValue(result, 0) 434 | << setToValue(underflow, 0) 435 | << assign(y, rhs) 436 | << assign(x, lhs) 437 | << "[" 438 | << safeDecr(y, underflow) 439 | << logicalOr(result, underflow) 440 | << movePtr(underflow) 441 | << "[" 442 | << setToValuePlus(y, 0) 443 | << setToValue(underflow, 0) 444 | << "]" 445 | << decr(x) 446 | << "]" 447 | << movePtr(result); 448 | 449 | return ops.str(); 450 | } 451 | 452 | std::string BFGenerator::less(int const lhs, int const rhs, int const result) 453 | { 454 | validateAddr(lhs, rhs, result); 455 | 456 | return greater(rhs, lhs, result); // reverse arguments 457 | } 458 | 459 | std::string BFGenerator::greaterOrEqual(int const lhs, int const rhs, int const result) 460 | { 461 | validateAddr(lhs, rhs, result); 462 | 463 | int const tmp = f_getTempBlock(2); 464 | int const isEqual = tmp + 0; 465 | int const isGreater = tmp + 1; 466 | 467 | std::ostringstream ops; 468 | ops << equal(lhs, rhs, isEqual) 469 | << greater(lhs, rhs, isGreater) 470 | << logicalOr(isEqual, isGreater, result); 471 | 472 | return ops.str(); 473 | } 474 | 475 | std::string BFGenerator::lessOrEqual(int const lhs, int const rhs, int const result) 476 | { 477 | validateAddr(lhs, rhs, result); 478 | 479 | return greaterOrEqual(rhs, lhs, result); // reverse arguments 480 | } 481 | 482 | std::string BFGenerator::fetchElement(int const arrStart, int const arrSize, int const index, int const ret) 483 | { 484 | // Algorithms to move an unknown amount to the left and right. 485 | // Assumes the pointer points to a cell containing the amount 486 | // it needs to be shifted and a copy of this amount adjacent to it. 487 | // Also, neighboring cells must all be zeroed out. 488 | 489 | static std::string const dynamicMoveRight = "[>[->+<]<[->+<]>-]"; 490 | static std::string const dynamicMoveLeft = "[<[-<+>]>[-<+>]<-]<"; 491 | 492 | // Allocate a buffer with 3 additional cells: 493 | // 1. to keep a copy of the index 494 | // 2. to store a temporary necessary for copying 495 | // 3. to prevent overflow on off-by-one errors 496 | 497 | int const bufSize = arrSize + 3; 498 | int const buf = f_getTempBlock(bufSize); 499 | int const dist = buf - arrStart; 500 | 501 | std::string const arr2buf(std::abs(dist), (dist > 0 ? '>' : '<')); 502 | std::string const buf2arr(std::abs(dist), (dist > 0 ? '<' : '>')); 503 | 504 | std::ostringstream ops; 505 | ops << assign(buf + 0, index) 506 | << assign(buf + 1, buf) 507 | << setToValue(buf + 2, 0, bufSize - 2) 508 | << movePtr(buf) 509 | << dynamicMoveRight 510 | << buf2arr 511 | << "[-" 512 | << arr2buf 513 | << ">>+<<" 514 | << buf2arr 515 | << "]" 516 | << arr2buf 517 | << ">>" 518 | << "[" 519 | << "-<<+" 520 | << buf2arr 521 | << "+" 522 | << arr2buf 523 | << ">>" 524 | << "]" 525 | << "<" 526 | << dynamicMoveLeft 527 | << assign(ret, buf); 528 | 529 | return ops.str(); 530 | } 531 | 532 | std::string BFGenerator::assignElement(int const arrStart, int const arrSize, int const index, int const val) 533 | { 534 | static std::string const dynamicMoveRight = "[>>[->+<]<[->+<]<[->+<]>-]"; 535 | static std::string const dynamicMoveLeft = "[[-<+>]<-]<"; 536 | 537 | int const bufSize = arrSize + 3; 538 | int const buf = f_getTempBlock(bufSize); 539 | int const dist = buf - arrStart; 540 | 541 | std::string const arr2buf(std::abs(dist), (dist > 0 ? '>' : '<')); 542 | std::string const buf2arr(std::abs(dist), (dist > 0 ? '<' : '>')); 543 | 544 | std::ostringstream ops; 545 | ops << assign(buf, index) 546 | << assign(buf + 1, buf) 547 | << assign(buf + 2, val) 548 | << setToValue(buf + 3, 0, bufSize - 3) 549 | << movePtr(buf) 550 | << dynamicMoveRight 551 | << buf2arr 552 | << "[-]" 553 | << arr2buf 554 | << ">>" 555 | << "[" 556 | << "-<<" 557 | << buf2arr 558 | << "+" 559 | << arr2buf 560 | << ">>" 561 | << "]" 562 | << "<" 563 | << dynamicMoveLeft; 564 | 565 | return ops.str(); 566 | } 567 | 568 | std::string BFGenerator::divmod(int const num, int const denom, int const divResult, int const modResult) 569 | { 570 | int const tmp = f_getTempBlock(4); 571 | int const tmp_loopflag = tmp + 0; 572 | int const tmp_zeroflag = tmp + 1; 573 | int const tmp_num = tmp + 2; 574 | int const tmp_denom = tmp + 3; 575 | 576 | // Algorithm: 577 | // 1. Initialize result-cells to 0 and copy operands to temps 578 | // 2. In case the denominator is 0 (divide by zero), set the result to 255 (~inf) 579 | // Set the loopflag to 0 in order to skip the calculating loop. 580 | // 3. In case the numerator is 0, the result of division is also zero. Set the remainder 581 | // to the same value as the denominator. 582 | // 4. Enter the loop: 583 | // * On each iteration, decrement both the denominator and enumerator, 584 | // until the denominator becomes 0.When this happens, increment result_div and 585 | // reset result_mod to 0. Also, reset the denominator to its original value. 586 | // 587 | // * The loop is broken when the enumerator has become zero. 588 | // By that time, we have counted how many times te denominator 589 | // fits inside the enumerator (result_div), and how many is left (result_mod). 590 | 591 | std::ostringstream ops; 592 | ops << setToValue(divResult, 0) // 1 593 | << setToValue(modResult, 0) 594 | << assign(tmp_num, num) 595 | << assign(tmp_denom, denom) 596 | << setToValue(tmp_loopflag, 1) 597 | << logicalNot(denom, tmp_zeroflag) 598 | << "[" // 2 599 | << setToValue(tmp_loopflag, 0) 600 | << setToValue(divResult, d_maxCellValue) 601 | << setToValue(modResult, d_maxCellValue) 602 | << setToValue(tmp_zeroflag, 0) 603 | << "]" 604 | << logicalNot(num, tmp_zeroflag) 605 | << "[" // 3 606 | << setToValue(tmp_loopflag, 0) 607 | << setToValue(divResult, 0) 608 | << setToValue(modResult, 0) 609 | << setToValue(tmp_zeroflag, 0) 610 | << "]" 611 | << movePtr(tmp_loopflag) 612 | << "[" // 4 613 | << decr(tmp_num) 614 | << decr(tmp_denom) 615 | << incr(modResult) 616 | << logicalNot(tmp_denom, tmp_zeroflag) 617 | << "[" 618 | << incr(divResult) 619 | << assign(tmp_denom, denom) 620 | << setToValue(modResult, 0) 621 | << setToValue(tmp_zeroflag, 0) 622 | << "]" 623 | << logicalNot(tmp_num, tmp_zeroflag) 624 | << "[" 625 | << setToValue(tmp_loopflag, 0) 626 | << setToValue(tmp_zeroflag, 0) 627 | << "]" 628 | << movePtr(tmp_loopflag) 629 | << "]"; 630 | 631 | return ops.str(); 632 | } 633 | -------------------------------------------------------------------------------- /src/compilerbase.h: -------------------------------------------------------------------------------- 1 | // Generated by Bisonc++ V6.03.00 on Thu, 07 Jul 2022 17:06:03 -46033506 2 | 3 | // hdr/includes 4 | #ifndef CompilerBase_h_included 5 | #define CompilerBase_h_included 6 | 7 | #include 8 | #include 9 | #include 10 | // $insert polyincludes 11 | #include 12 | // $insert preincludes 13 | #include "parser_types.h" 14 | 15 | // hdr/baseclass 16 | 17 | namespace // anonymous 18 | { 19 | struct PI_; 20 | } 21 | 22 | 23 | // $insert polymorphic 24 | enum class Tag_ 25 | { 26 | INT, 27 | DECLARATION_LIST, 28 | PARAMETER_LIST, 29 | CASE_LIST, 30 | BFX_FUNCTION, 31 | STRING, 32 | DECLARATION, 33 | STRING_LIST, 34 | INSTRUCTION, 35 | PARAMETER, 36 | CHAR, 37 | INSTRUCTION_LIST, 38 | TEST_VECTOR, 39 | STRING_TUPLE, 40 | INSTRUCTION_PAIR, 41 | }; 42 | 43 | namespace Meta_ 44 | { 45 | 46 | extern size_t const *t_nErrors; 47 | 48 | extern size_t const *s_nErrors_; 49 | 50 | template 51 | struct TypeOf; 52 | 53 | template 54 | struct TagOf; 55 | 56 | // $insert polymorphicSpecializations 57 | enum { sizeofTag_ = 15 }; 58 | 59 | extern char const *idOfTag_[]; 60 | template <> 61 | struct TagOf 62 | { 63 | static Tag_ const tag = Tag_::INT; 64 | }; 65 | 66 | template <> 67 | struct TagOf>> 68 | { 69 | static Tag_ const tag = Tag_::DECLARATION_LIST; 70 | }; 71 | 72 | template <> 73 | struct TagOf> 74 | { 75 | static Tag_ const tag = Tag_::PARAMETER_LIST; 76 | }; 77 | 78 | template <> 79 | struct TagOf>> 80 | { 81 | static Tag_ const tag = Tag_::CASE_LIST; 82 | }; 83 | 84 | template <> 85 | struct TagOf 86 | { 87 | static Tag_ const tag = Tag_::BFX_FUNCTION; 88 | }; 89 | 90 | template <> 91 | struct TagOf 92 | { 93 | static Tag_ const tag = Tag_::STRING; 94 | }; 95 | 96 | template <> 97 | struct TagOf> 98 | { 99 | static Tag_ const tag = Tag_::DECLARATION; 100 | }; 101 | 102 | template <> 103 | struct TagOf> 104 | { 105 | static Tag_ const tag = Tag_::STRING_LIST; 106 | }; 107 | 108 | template <> 109 | struct TagOf 110 | { 111 | static Tag_ const tag = Tag_::INSTRUCTION; 112 | }; 113 | 114 | template <> 115 | struct TagOf 116 | { 117 | static Tag_ const tag = Tag_::PARAMETER; 118 | }; 119 | 120 | template <> 121 | struct TagOf 122 | { 123 | static Tag_ const tag = Tag_::CHAR; 124 | }; 125 | 126 | template <> 127 | struct TagOf> 128 | { 129 | static Tag_ const tag = Tag_::INSTRUCTION_LIST; 130 | }; 131 | 132 | template <> 133 | struct TagOf>> 134 | { 135 | static Tag_ const tag = Tag_::TEST_VECTOR; 136 | }; 137 | 138 | template <> 139 | struct TagOf> 140 | { 141 | static Tag_ const tag = Tag_::STRING_TUPLE; 142 | }; 143 | 144 | template <> 145 | struct TagOf> 146 | { 147 | static Tag_ const tag = Tag_::INSTRUCTION_PAIR; 148 | }; 149 | 150 | template <> 151 | struct TypeOf 152 | { 153 | typedef int type; 154 | }; 155 | 156 | template <> 157 | struct TypeOf 158 | { 159 | typedef std::vector> type; 160 | }; 161 | 162 | template <> 163 | struct TypeOf 164 | { 165 | typedef std::vector type; 166 | }; 167 | 168 | template <> 169 | struct TypeOf 170 | { 171 | typedef std::vector> type; 172 | }; 173 | 174 | template <> 175 | struct TypeOf 176 | { 177 | typedef BFXFunction type; 178 | }; 179 | 180 | template <> 181 | struct TypeOf 182 | { 183 | typedef std::string type; 184 | }; 185 | 186 | template <> 187 | struct TypeOf 188 | { 189 | typedef std::pair type; 190 | }; 191 | 192 | template <> 193 | struct TypeOf 194 | { 195 | typedef std::vector type; 196 | }; 197 | 198 | template <> 199 | struct TypeOf 200 | { 201 | typedef Instruction type; 202 | }; 203 | 204 | template <> 205 | struct TypeOf 206 | { 207 | typedef BFXFunction::Parameter type; 208 | }; 209 | 210 | template <> 211 | struct TypeOf 212 | { 213 | typedef char type; 214 | }; 215 | 216 | template <> 217 | struct TypeOf 218 | { 219 | typedef std::vector type; 220 | }; 221 | 222 | template <> 223 | struct TypeOf 224 | { 225 | typedef std::vector> type; 226 | }; 227 | 228 | template <> 229 | struct TypeOf 230 | { 231 | typedef std::tuple type; 232 | }; 233 | 234 | template <> 235 | struct TypeOf 236 | { 237 | typedef std::pair type; 238 | }; 239 | 240 | 241 | // Individual semantic value classes are derived from Base, offering a 242 | // member returning the value's Tag_, a member cloning the object of its 243 | // derived Semantic and a member returning a pointerr to its 244 | // derived Semantic data. See also Bisonc++'s distribution file 245 | // README.polymorphic-techical 246 | class Base 247 | { 248 | protected: 249 | Tag_ d_baseTag; // d_baseTag is assigned by Semantic. 250 | 251 | public: 252 | Base() = default; 253 | Base(Base const &other) = delete; 254 | 255 | virtual ~Base(); 256 | 257 | Tag_ tag() const; 258 | Base *clone() const; 259 | void *data() const; 260 | 261 | private: 262 | virtual Base *vClone() const = 0; 263 | virtual void *vData() const = 0; 264 | }; 265 | 266 | inline Base *Base::clone() const 267 | { 268 | return vClone(); 269 | } 270 | 271 | inline void *Base::data() const 272 | { 273 | return vData(); 274 | } 275 | 276 | inline Tag_ Base::tag() const 277 | { 278 | return d_baseTag; 279 | } 280 | 281 | // The class Semantic stores a semantic value of the type matching tg_ 282 | template 283 | class Semantic: public Base 284 | { 285 | typename TypeOf::type d_data; 286 | 287 | public: 288 | Semantic(); 289 | Semantic(Semantic const &other); // req'd for cloning 290 | 291 | // This constructor member template forwards its arguments to 292 | // d_data, allowing it to be initialized using whatever 293 | // constructor is available for DataType 294 | template 295 | Semantic(Params &&...params); 296 | 297 | private: 298 | Base *vClone() const override; 299 | void *vData() const override; 300 | }; 301 | 302 | template 303 | Semantic::Semantic() 304 | { 305 | d_baseTag = tg_; // Base's data member: 306 | } 307 | 308 | template 309 | Semantic::Semantic(Semantic const &other) 310 | : 311 | d_data(other.d_data) 312 | { 313 | d_baseTag = other.d_baseTag; 314 | } 315 | 316 | template 317 | template 318 | Semantic::Semantic(Params &&...params) 319 | : 320 | d_data(std::forward(params) ...) 321 | { 322 | d_baseTag = tg_; 323 | } 324 | 325 | 326 | template 327 | Base *Semantic::vClone() const 328 | { 329 | return new Semantic{*this}; 330 | } 331 | 332 | template 333 | void *Semantic::vData() const 334 | { 335 | return const_cast::type *>(&d_data); 336 | } 337 | 338 | 339 | // The class SType wraps a pointer to Base. It becomes the polymorphic 340 | // STYPE_ type. It also defines get members, allowing constructions like 341 | // $$.get to be used. 342 | class SType: private std::unique_ptr 343 | { 344 | typedef std::unique_ptr BasePtr; 345 | 346 | public: 347 | SType() = default; 348 | SType(SType const &other); 349 | SType(SType &&tmp); 350 | 351 | ~SType() = default; 352 | 353 | // Specific overloads are needed for SType = SType assignments 354 | SType &operator=(SType const &rhs); 355 | SType &operator=(SType &rhs); // required so it is used 356 | // instead of the template op= 357 | SType &operator=(SType &&tmp); 358 | 359 | // $insert polymorphicOpAssignDecl 360 | SType &operator=(int const &value); 361 | SType &operator=(int &&tmp); 362 | 363 | SType &operator=(std::vector> const &value); 364 | SType &operator=(std::vector> &&tmp); 365 | 366 | SType &operator=(std::vector const &value); 367 | SType &operator=(std::vector &&tmp); 368 | 369 | SType &operator=(std::vector> const &value); 370 | SType &operator=(std::vector> &&tmp); 371 | 372 | SType &operator=(BFXFunction const &value); 373 | SType &operator=(BFXFunction &&tmp); 374 | 375 | SType &operator=(std::string const &value); 376 | SType &operator=(std::string &&tmp); 377 | 378 | SType &operator=(std::pair const &value); 379 | SType &operator=(std::pair &&tmp); 380 | 381 | SType &operator=(std::vector const &value); 382 | SType &operator=(std::vector &&tmp); 383 | 384 | SType &operator=(Instruction const &value); 385 | SType &operator=(Instruction &&tmp); 386 | 387 | SType &operator=(BFXFunction::Parameter const &value); 388 | SType &operator=(BFXFunction::Parameter &&tmp); 389 | 390 | SType &operator=(char const &value); 391 | SType &operator=(char &&tmp); 392 | 393 | SType &operator=(std::vector const &value); 394 | SType &operator=(std::vector &&tmp); 395 | 396 | SType &operator=(std::vector> const &value); 397 | SType &operator=(std::vector> &&tmp); 398 | 399 | SType &operator=(std::tuple const &value); 400 | SType &operator=(std::tuple &&tmp); 401 | 402 | SType &operator=(std::pair const &value); 403 | SType &operator=(std::pair &&tmp); 404 | 405 | template 406 | void assign(Args &&...args); 407 | 408 | // By default the get()-members check whether the specified 409 | // matches the tag returned by SType::tag (d_data's tag). If they 410 | // don't match a run-time fatal error results. 411 | template 412 | typename TypeOf::type &get(); 413 | 414 | template 415 | typename TypeOf::type const &get() const; 416 | 417 | Tag_ tag() const; 418 | bool valid() const; 419 | }; 420 | 421 | inline SType::SType(SType const &other) 422 | : 423 | BasePtr{other ? other->clone() : 0} 424 | {} 425 | 426 | inline SType::SType(SType &&tmp) 427 | : 428 | BasePtr{std::move(tmp)} 429 | {} 430 | 431 | inline SType &SType::operator=(SType const &rhs) 432 | { 433 | reset(rhs->clone()); 434 | return *this; 435 | } 436 | 437 | inline SType &SType::operator=(SType &rhs) 438 | { 439 | reset(rhs->clone()); 440 | return *this; 441 | } 442 | 443 | inline SType &SType::operator=(SType &&tmp) 444 | { 445 | BasePtr::operator=(std::move(tmp)); 446 | return *this; 447 | } 448 | 449 | // $insert polymorphicOpAssignImpl 450 | inline SType &SType::operator=(int const &value) 451 | { 452 | assign< Tag_::INT >(value); 453 | return *this; 454 | } 455 | inline SType &SType::operator=(int &&tmp) 456 | { 457 | assign< Tag_::INT >(std::move(tmp)); 458 | return *this; 459 | } 460 | inline SType &SType::operator=(std::vector> const &value) 461 | { 462 | assign< Tag_::DECLARATION_LIST >(value); 463 | return *this; 464 | } 465 | inline SType &SType::operator=(std::vector> &&tmp) 466 | { 467 | assign< Tag_::DECLARATION_LIST >(std::move(tmp)); 468 | return *this; 469 | } 470 | inline SType &SType::operator=(std::vector const &value) 471 | { 472 | assign< Tag_::PARAMETER_LIST >(value); 473 | return *this; 474 | } 475 | inline SType &SType::operator=(std::vector &&tmp) 476 | { 477 | assign< Tag_::PARAMETER_LIST >(std::move(tmp)); 478 | return *this; 479 | } 480 | inline SType &SType::operator=(std::vector> const &value) 481 | { 482 | assign< Tag_::CASE_LIST >(value); 483 | return *this; 484 | } 485 | inline SType &SType::operator=(std::vector> &&tmp) 486 | { 487 | assign< Tag_::CASE_LIST >(std::move(tmp)); 488 | return *this; 489 | } 490 | inline SType &SType::operator=(BFXFunction const &value) 491 | { 492 | assign< Tag_::BFX_FUNCTION >(value); 493 | return *this; 494 | } 495 | inline SType &SType::operator=(BFXFunction &&tmp) 496 | { 497 | assign< Tag_::BFX_FUNCTION >(std::move(tmp)); 498 | return *this; 499 | } 500 | inline SType &SType::operator=(std::string const &value) 501 | { 502 | assign< Tag_::STRING >(value); 503 | return *this; 504 | } 505 | inline SType &SType::operator=(std::string &&tmp) 506 | { 507 | assign< Tag_::STRING >(std::move(tmp)); 508 | return *this; 509 | } 510 | inline SType &SType::operator=(std::pair const &value) 511 | { 512 | assign< Tag_::DECLARATION >(value); 513 | return *this; 514 | } 515 | inline SType &SType::operator=(std::pair &&tmp) 516 | { 517 | assign< Tag_::DECLARATION >(std::move(tmp)); 518 | return *this; 519 | } 520 | inline SType &SType::operator=(std::vector const &value) 521 | { 522 | assign< Tag_::STRING_LIST >(value); 523 | return *this; 524 | } 525 | inline SType &SType::operator=(std::vector &&tmp) 526 | { 527 | assign< Tag_::STRING_LIST >(std::move(tmp)); 528 | return *this; 529 | } 530 | inline SType &SType::operator=(Instruction const &value) 531 | { 532 | assign< Tag_::INSTRUCTION >(value); 533 | return *this; 534 | } 535 | inline SType &SType::operator=(Instruction &&tmp) 536 | { 537 | assign< Tag_::INSTRUCTION >(std::move(tmp)); 538 | return *this; 539 | } 540 | inline SType &SType::operator=(BFXFunction::Parameter const &value) 541 | { 542 | assign< Tag_::PARAMETER >(value); 543 | return *this; 544 | } 545 | inline SType &SType::operator=(BFXFunction::Parameter &&tmp) 546 | { 547 | assign< Tag_::PARAMETER >(std::move(tmp)); 548 | return *this; 549 | } 550 | inline SType &SType::operator=(char const &value) 551 | { 552 | assign< Tag_::CHAR >(value); 553 | return *this; 554 | } 555 | inline SType &SType::operator=(char &&tmp) 556 | { 557 | assign< Tag_::CHAR >(std::move(tmp)); 558 | return *this; 559 | } 560 | inline SType &SType::operator=(std::vector const &value) 561 | { 562 | assign< Tag_::INSTRUCTION_LIST >(value); 563 | return *this; 564 | } 565 | inline SType &SType::operator=(std::vector &&tmp) 566 | { 567 | assign< Tag_::INSTRUCTION_LIST >(std::move(tmp)); 568 | return *this; 569 | } 570 | inline SType &SType::operator=(std::vector> const &value) 571 | { 572 | assign< Tag_::TEST_VECTOR >(value); 573 | return *this; 574 | } 575 | inline SType &SType::operator=(std::vector> &&tmp) 576 | { 577 | assign< Tag_::TEST_VECTOR >(std::move(tmp)); 578 | return *this; 579 | } 580 | inline SType &SType::operator=(std::tuple const &value) 581 | { 582 | assign< Tag_::STRING_TUPLE >(value); 583 | return *this; 584 | } 585 | inline SType &SType::operator=(std::tuple &&tmp) 586 | { 587 | assign< Tag_::STRING_TUPLE >(std::move(tmp)); 588 | return *this; 589 | } 590 | inline SType &SType::operator=(std::pair const &value) 591 | { 592 | assign< Tag_::INSTRUCTION_PAIR >(value); 593 | return *this; 594 | } 595 | inline SType &SType::operator=(std::pair &&tmp) 596 | { 597 | assign< Tag_::INSTRUCTION_PAIR >(std::move(tmp)); 598 | return *this; 599 | } 600 | 601 | template 602 | void SType::assign(Args &&...args) 603 | { 604 | reset(new Semantic(std::forward(args) ...)); 605 | } 606 | 607 | template 608 | typename TypeOf::type &SType::get() 609 | { 610 | // $insert warnTagMismatches 611 | 612 | if (tag() != tg) 613 | { 614 | if (*t_nErrors != 0) 615 | const_cast(this)->assign(); 616 | else 617 | { 618 | std::cerr << "[Fatal] calling `.get(tg)] << 620 | ">()', but Tag " << 621 | idOfTag_[static_cast(tag())] << " is encountered. Try " 622 | "option --debug and call setDebug(Parser::ACTIONCASES)\n"; 623 | throw 1; // ABORTs 624 | } 625 | } 626 | 627 | return *static_cast::type *>( (*this)->data() ); 628 | } 629 | 630 | template 631 | typename TypeOf::type const &SType::get() const 632 | { 633 | // $insert warnTagMismatches 634 | 635 | if (tag() != tg) 636 | { 637 | if (*t_nErrors != 0) 638 | const_cast(this)->assign(); 639 | else 640 | { 641 | std::cerr << "[Fatal] calling `.get(tg)] << 643 | ">()', but Tag " << 644 | idOfTag_[static_cast(tag())] << " is encountered. Try " 645 | "option --debug and call setDebug(Parser::ACTIONCASES)\n"; 646 | throw 1; // ABORTs 647 | } 648 | } 649 | 650 | return *static_cast::type *>( (*this)->data() ); 651 | } 652 | 653 | inline Tag_ SType::tag() const 654 | { 655 | return valid() ? (*this)->tag() : static_cast(sizeofTag_); 656 | } 657 | 658 | inline bool SType::valid() const 659 | { 660 | return BasePtr::get() != 0; 661 | } 662 | 663 | } // namespace Meta_ 664 | 665 | class CompilerBase 666 | { 667 | public: 668 | enum DebugMode_ 669 | { 670 | OFF = 0, 671 | ON = 1 << 0, 672 | ACTIONCASES = 1 << 1 673 | }; 674 | 675 | // $insert tokens 676 | 677 | // Symbolic tokens: 678 | enum Tokens_ 679 | { 680 | LET = 257, 681 | FUNCTION, 682 | GLOBAL, 683 | INCLUDE, 684 | SCAN, 685 | PRINT, 686 | RAND, 687 | SIZEOF, 688 | FOR, 689 | IF, 690 | WHILE, 691 | CONST, 692 | SWITCH, 693 | CASE, 694 | DEFAULT, 695 | STRUCT, 696 | BREAK, 697 | CONTINUE, 698 | RETURN, 699 | ASSERT, 700 | START_TEST, 701 | END_TEST, 702 | INPUT, 703 | EXPECT, 704 | END_TEST_CONTENT, 705 | then, 706 | ELSE, 707 | _var, 708 | _arr, 709 | ADD, 710 | SUB, 711 | MUL, 712 | MOD, 713 | DIV, 714 | DIVMOD, 715 | MODDIV, 716 | POW, 717 | OR, 718 | AND, 719 | EQ, 720 | NE, 721 | LE, 722 | GE, 723 | unaryMinus, 724 | INC, 725 | DEC, 726 | IDENT, 727 | STR, 728 | NUM, 729 | CHR, 730 | }; 731 | 732 | // $insert STYPE 733 | typedef Meta_::SType STYPE_; 734 | 735 | 736 | private: 737 | // state semval 738 | typedef std::pair StatePair; 739 | // token semval 740 | typedef std::pair TokenPair; 741 | 742 | int d_stackIdx = -1; 743 | std::vector d_stateStack; 744 | StatePair *d_vsp = 0; // points to the topmost value stack 745 | size_t d_state = 0; 746 | 747 | TokenPair d_next; 748 | int d_token; 749 | 750 | bool d_terminalToken = false; 751 | bool d_recovery = false; 752 | 753 | 754 | protected: 755 | enum Return_ 756 | { 757 | PARSE_ACCEPT_ = 0, // values used as parse()'s return values 758 | PARSE_ABORT_ = 1 759 | }; 760 | enum ErrorRecovery_ 761 | { 762 | UNEXPECTED_TOKEN_, 763 | }; 764 | 765 | bool d_actionCases_ = false; // set by options/directives 766 | bool d_debug_ = true; 767 | size_t d_requiredTokens_; 768 | size_t d_nErrors_; // initialized by clearin() 769 | size_t d_acceptedTokens_; 770 | STYPE_ d_val_; 771 | 772 | 773 | CompilerBase(); 774 | 775 | void ABORT() const; 776 | void ACCEPT() const; 777 | void ERROR() const; 778 | 779 | STYPE_ &vs_(int idx); // value stack element idx 780 | int lookup_() const; 781 | int savedToken_() const; 782 | int token_() const; 783 | size_t stackSize_() const; 784 | size_t state_() const; 785 | size_t top_() const; 786 | void clearin_(); 787 | void errorVerbose_(); 788 | void lex_(int token); 789 | void popToken_(); 790 | void pop_(size_t count = 1); 791 | void pushToken_(int token); 792 | void push_(size_t nextState); 793 | void redoToken_(); 794 | bool recovery_() const; 795 | void reduce_(int rule); 796 | void shift_(int state); 797 | void startRecovery_(); 798 | 799 | public: 800 | void setDebug(bool mode); 801 | void setDebug(DebugMode_ mode); 802 | }; 803 | 804 | // hdr/abort 805 | inline void CompilerBase::ABORT() const 806 | { 807 | throw PARSE_ABORT_; 808 | } 809 | 810 | // hdr/accept 811 | inline void CompilerBase::ACCEPT() const 812 | { 813 | throw PARSE_ACCEPT_; 814 | } 815 | 816 | 817 | // hdr/error 818 | inline void CompilerBase::ERROR() const 819 | { 820 | throw UNEXPECTED_TOKEN_; 821 | } 822 | 823 | // hdr/savedtoken 824 | inline int CompilerBase::savedToken_() const 825 | { 826 | return d_next.first; 827 | } 828 | 829 | // hdr/opbitand 830 | inline CompilerBase::DebugMode_ operator&(CompilerBase::DebugMode_ lhs, 831 | CompilerBase::DebugMode_ rhs) 832 | { 833 | return static_cast( 834 | static_cast(lhs) & rhs); 835 | } 836 | 837 | // hdr/opbitor 838 | inline CompilerBase::DebugMode_ operator|(CompilerBase::DebugMode_ lhs, 839 | CompilerBase::DebugMode_ rhs) 840 | { 841 | return static_cast(static_cast(lhs) | rhs); 842 | }; 843 | 844 | // hdr/recovery 845 | inline bool CompilerBase::recovery_() const 846 | { 847 | return d_recovery; 848 | } 849 | 850 | // hdr/stacksize 851 | inline size_t CompilerBase::stackSize_() const 852 | { 853 | return d_stackIdx + 1; 854 | } 855 | 856 | // hdr/state 857 | inline size_t CompilerBase::state_() const 858 | { 859 | return d_state; 860 | } 861 | 862 | // hdr/token 863 | inline int CompilerBase::token_() const 864 | { 865 | return d_token; 866 | } 867 | 868 | // hdr/vs 869 | inline CompilerBase::STYPE_ &CompilerBase::vs_(int idx) 870 | { 871 | return (d_vsp + idx)->second; 872 | } 873 | 874 | // hdr/tail 875 | // For convenience, when including ParserBase.h its symbols are available as 876 | // symbols in the class Parser, too. 877 | #define Compiler CompilerBase 878 | 879 | 880 | #endif 881 | 882 | 883 | 884 | --------------------------------------------------------------------------------