├── .gitignore ├── .travis.yml ├── README.md ├── doc └── basilc.1 ├── examples ├── colors.basilc ├── helloworld.basilc └── name.basilc ├── include ├── cmd.h ├── libbasilc │ ├── controlflow.h │ ├── io.h │ ├── libbasilc.h │ ├── system.h │ └── variable.h ├── main.h └── stringhelpers.h ├── makefile └── src ├── cmd.c ├── libbasilc ├── controlflow.c ├── io.c ├── libbasilc.c ├── make.config ├── system.c └── variable.c ├── main.c └── stringhelpers.c /.gitignore: -------------------------------------------------------------------------------- 1 | #ignore compiled executable 2 | out/* 3 | *.o 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: gcc 3 | script: make 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BasilC 2 | ======= 3 | 4 | [![Build Status](https://travis-ci.org/shawnanastasio/BasilC.svg?branch=master)](https://travis-ci.org/shawnanastasio/BasilC) 5 | 6 | BasilC is an esoteric interpreted programming language aimed at rapid development and deployment. 7 | BasilC introduces the new programming paradigm of procedural non-typed languages. 8 | 9 | Quick start 10 | ----------- 11 | 12 | Since The BasilC interpreter is written in pure C, it can be compiled with 0 dependencies on nearly any platform with a C compiler. 13 | 14 | To compile on UNIX-like systems with GNU Make, simply run: 15 | ``` 16 | $ make 17 | ``` 18 | 19 | If you wish to use shebang lines (such as the one in example.basilc) to execute BasilC programs as scripts, or if you wish to read the manpage with the man command, install the compiled binary to your path: 20 | ``` 21 | $ sudo make install 22 | ``` 23 | 24 | You can test your installation by running the test script in the `examples` folder: 25 | ``` 26 | $ basilc helloworld.basilc 27 | ``` 28 | Or, alternatively: 29 | ``` 30 | $ chmod +x helloworld.basilc 31 | $ ./helloworld.basilc 32 | ``` 33 | 34 | Documentation is available in the manpage format, and it can be accessed with the following command: 35 | ``` 36 | man basilc 37 | ``` 38 | 39 | 40 | Technical Explanation 41 | --------------------- 42 | The BasilC interpreter is written in 100% C in order to provide fast run times and portability 43 | across nearly all POSIX platforms, and even Windows. 44 | 45 | _BasilC abstains from traditional programming paradigms and puts the static vs dynamically typed variable debate to an end once and for all._ 46 | 47 | All variables in BasilC are stored internally as char literals. This provides enhanced flexibility 48 | for all types of data, while still allowing for type-dependent operations, such as arithmetic, that 49 | can still be performed by leveraging C's powerful casting system on a per-function basis. 50 | -------------------------------------------------------------------------------- /doc/basilc.1: -------------------------------------------------------------------------------- 1 | .TH basilc 1 "July 15, 2016" "version 1.0" "USER COMMANDS" 2 | .SH NAME 3 | basilc \- An interpreter for the BasilC esoteric programming language 4 | .SH SYNOPSIS 5 | .B basilc 6 | [\-m] [\-d] [\-t] file 7 | .SH DESCRIPTION 8 | BasilC is an esoteric interpreted programming language aimed at rapid development and deployment. BasilC introduces the new programming paradigm of procedural non-typed languages. Please visit the examples directory of the source code to view example programs written in BasilC. 9 | .SH OPTIONS 10 | .TP 11 | \-m 12 | enables monochrome mode. This disables ANSI terminal escape codes 13 | .TP 14 | \-d 15 | disables debugging and error output while interpreting a .basilc script 16 | .TP 17 | \-t 18 | shows the total time elapsed in seconds from the start of the BasilC interpreter to the completion of the running .basilc script 19 | .PP 20 | .SH COMMANDS 21 | Please note that the BasilC- prefix is fully optional in recent versions of the BasilC interpreter! 22 | .TP 23 | BasilC-#// \- Single line comment, interpreter ignores everything after this until the next line 24 | .TP 25 | BasilC-ask(question, variable) \- Prints out the given question and allows the user to input an answer, which is promptly stored to the given variable 26 | .TP 27 | BasilC-define(variable, value) \- Sets the given variable to the given value, all variables in BasilC are internally stored as character arrays 28 | .TP 29 | BasilC-end() \- Stops execution of the running program. Please note that execution terminates at the end of the program source file, with or without this statement's presence 30 | .TP 31 | BasilC-endif() \- Marks the end of a code block executed by the BasilC-if() condition test 32 | .TP 33 | BasilC-goto(label) \- Jumps code execution to the line of code marked by a BasilC-label() of the given name 34 | .TP 35 | BasilC-if(condition) \- Tests if the given mathematical condition (formatted as '6 > 7' or the like) is true, and if so executes all code until the next BasilC-endif(). If false, code execution jumps to the line after the next BasilC-endif() 36 | .TP 37 | BasilC-label(label) \- Marks a line of code as a location that code execution can jump to with a BasilC-goto() of the given name, this command does not execute any code 38 | .TP 39 | BasilC-naptime(n) \- Sleep for n seconds 40 | .TP 41 | BasilC-say(Hello $World) \- Prints out the given text to the terminal, substituting any word prefaced by a $ with the value of the variable named so 42 | .TP 43 | BasilC-sayln(Hello $World) \- Prints out the given text as BasilC-say() does, starting a newline after the printed text 44 | .TP 45 | BasilC-tint(color) \- Sets the terminal foreground printing color to the one specified, does nothing if monochrome mode option [-m] is set, see SUPPORTED COLORS in the APPENDIX for a list of supported colors. If an invalid color is specified, such as RESET, then the terminal is reset to its original color scheme. This reset occurs at the end of a program's execution 46 | .TP 47 | BasilC-tintbg(color) \- Sets the terminal background printing color in a manner similar to BasilC-tint() 48 | .TP 49 | BasilC-yolo(command) \- Executes the given command in the host shell. Please note that this can allow malicious commands and/or code to be run, and use this command with caution 50 | .PP 51 | .SH APPENDIX 52 | .TP 53 | .SH I: RUNNING PROGRAMS 54 | There are two ways to execute BasilC programs, the first way is to use the `basilc` command to run a program as specified in the SYNOPSIS section. The second way is to prepend a shebang line to the BasilC program source file, then to allow the file to be executed (chmod +x), and then to run the source file with `./example.basilc`. An example shebang line is `#!/usr/local/bin/basilc`, which will allow the source file to execute itself as long as the interpreter has been installed to your PATH during installation 55 | .TP 56 | .SH II: SUPPORTED COLORS 57 | black red green yellow blue magenta cyan white 58 | .PP 59 | .SH INTERNET RESOURCES 60 | https://github.com/shawnanastasio/BasilC 61 | -------------------------------------------------------------------------------- /examples/colors.basilc: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/basilc 2 | BasilC#// ANSI color code test in BasilC 3 | 4 | BasilC-tintbg(cyan) 5 | BasilC-tint(red) 6 | BasilC-say(red ) 7 | 8 | BasilC-tintbg(red) 9 | BasilC-tint(Blue) 10 | BasilC-say(Blue ) 11 | 12 | BasilC-tintbg(black) 13 | BasilC-tint(YELLOW) 14 | BasilC-sayln(YELLOW) 15 | 16 | BasilC-tint(reset) 17 | BasilC-sayln(Colors reset!) 18 | -------------------------------------------------------------------------------- /examples/helloworld.basilc: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/basilc 2 | BasilC#// Hello World program implemented in BasilC 3 | 4 | BasilC-sayln(Hello, World!) 5 | -------------------------------------------------------------------------------- /examples/name.basilc: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/basilc 2 | 3 | BasilC#// Basic console input program in BasilC 4 | BasilC-define(name, temp) 5 | BasilC-sayln(Hello my name is BasilC!) 6 | BasilC-ask(What is your name?: , name) 7 | BasilC-sayln(Glad to meet you, $name) 8 | BasilC-end() 9 | -------------------------------------------------------------------------------- /include/cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | enum parse_error { 9 | ERR_SUCCESS, 10 | ERR_PAREN, 11 | ERR_INVALID_CMD, 12 | ERR_ARGS, 13 | ERR_SPECIAL_PARSE 14 | }; 15 | 16 | extern char parse_error_msgs[][32]; 17 | 18 | struct cmd_declaration { 19 | char *name; 20 | int8_t num_args; 21 | bool (*handle_cmd)(stack_node_t **); 22 | bool (*special_parse)(void); 23 | }; 24 | typedef struct cmd_declaration cmd_declaration_t; 25 | 26 | struct registered_cmd_stack { 27 | char *name; 28 | int8_t num_args; 29 | bool (*handle_cmd)(stack_node_t **); 30 | bool (*special_parse)(void); 31 | struct registered_cmd_stack *next; 32 | }; 33 | typedef struct registered_cmd_stack registered_cmd_stack_t; 34 | 35 | extern registered_cmd_stack_t *root_cmd; 36 | extern registered_cmd_stack_t *current_cmd_stack; 37 | 38 | void init_cmd_stack(); 39 | void register_cmd(cmd_declaration_t *dec); 40 | registered_cmd_stack_t * cmd_stack_search_label(char *label); 41 | int32_t parse_user_command(char *input, int32_t input_len); 42 | bool execute_command(stack_node_t **node); 43 | void __debug_print_cmd_stack(); 44 | -------------------------------------------------------------------------------- /include/libbasilc/controlflow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | // Definition for BasilC-if() 9 | bool basilc_if_callback(stack_node_t **node); 10 | bool basilc_if_special_parse(); 11 | cmd_declaration_t basilc_if = { 12 | .name = "if", 13 | .num_args = 1, 14 | .handle_cmd = basilc_if_callback, 15 | .special_parse = basilc_if_special_parse, 16 | }; 17 | 18 | // Definition of BasilC-endif() 19 | bool basilc_endif_special_parse(); 20 | cmd_declaration_t basilc_endif = { 21 | .name = "endif", 22 | .num_args = 0, 23 | .special_parse = basilc_endif_special_parse, 24 | }; 25 | 26 | // Definition of BasilC-label() 27 | bool basilc_label_callback(stack_node_t **node); 28 | cmd_declaration_t basilc_label = { 29 | .name = "label", 30 | .num_args = 1, 31 | }; 32 | 33 | // Definition of BasilC-goto() 34 | bool basilc_goto_callback(stack_node_t **node); 35 | cmd_declaration_t basilc_goto = { 36 | .name = "goto", 37 | .num_args = 1, 38 | .handle_cmd = basilc_goto_callback, 39 | }; 40 | 41 | // Definition for BasilC-end() 42 | bool basilc_end_callback(stack_node_t **node); 43 | cmd_declaration_t basilc_end = { 44 | .name = "end", 45 | .num_args = 0, 46 | .handle_cmd = basilc_end_callback, 47 | }; 48 | -------------------------------------------------------------------------------- /include/libbasilc/io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | // Definition for BasilC-say() 9 | bool basilc_say_callback(stack_node_t **node); 10 | cmd_declaration_t basilc_say = { 11 | .name = "say", 12 | .num_args = -1, 13 | .handle_cmd = basilc_say_callback, 14 | }; 15 | 16 | // Definition for BasilC-sayln() 17 | bool basilc_sayln_callback(stack_node_t **node); 18 | cmd_declaration_t basilc_sayln = { 19 | .name = "sayln", 20 | .num_args = -1, 21 | .handle_cmd = basilc_sayln_callback, 22 | }; 23 | 24 | // Definition for BasilC-tint() 25 | bool basilc_tint_callback(stack_node_t **node); 26 | cmd_declaration_t basilc_tint = { 27 | .name = "tint", 28 | .num_args = 1, 29 | .handle_cmd = basilc_tint_callback, 30 | }; 31 | 32 | // Definition for BasilC-tintbg() 33 | bool basilc_tintbg_callback(stack_node_t **node); 34 | cmd_declaration_t basilc_tintbg = { 35 | .name = "tintbg", 36 | .num_args = 1, 37 | .handle_cmd = basilc_tintbg_callback, 38 | }; 39 | 40 | void basilc_handle_tint(char code, char *temp); 41 | 42 | // Definition for BasilC-ask() 43 | bool basilc_ask_callback(stack_node_t **node); 44 | cmd_declaration_t basilc_ask = { 45 | .name = "ask", 46 | .num_args = 2, 47 | .handle_cmd = basilc_ask_callback, 48 | }; 49 | -------------------------------------------------------------------------------- /include/libbasilc/libbasilc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void libbasilc_register(); 4 | -------------------------------------------------------------------------------- /include/libbasilc/system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | // Definition for BasilC-yolo() 9 | bool basilc_yolo_callback(stack_node_t **node); 10 | cmd_declaration_t basilc_yolo = { 11 | .name = "yolo", 12 | .num_args = -1, 13 | .handle_cmd = basilc_yolo_callback, 14 | }; 15 | 16 | // Definition for BasilC-naptime() 17 | bool basilc_naptime_callback(stack_node_t **node); 18 | cmd_declaration_t basilc_naptime = { 19 | .name = "naptime", 20 | .num_args = 1, 21 | .handle_cmd = basilc_naptime_callback, 22 | }; 23 | -------------------------------------------------------------------------------- /include/libbasilc/variable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | // Definition for BasilC-define() 9 | bool basilc_define_callback(stack_node_t **node); 10 | cmd_declaration_t basilc_define = { 11 | .name = "define", 12 | .num_args = 2, 13 | .handle_cmd = basilc_define_callback, 14 | }; 15 | -------------------------------------------------------------------------------- /include/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define STACK_PARAMETER_MAX_AMOUNT 5 // Max parameters a command can have 7 | #define STACK_PARAMETER_MAX_LENGTH 100 // Max length of a parameter 8 | 9 | #define MAX_DATA_SIZE 32 10 | 11 | // General stack node contains command and pointer to parameter linked list 12 | struct stack_node { 13 | char *command; 14 | bool execute; 15 | char parameters[STACK_PARAMETER_MAX_AMOUNT][STACK_PARAMETER_MAX_LENGTH]; 16 | struct stack_node *next; 17 | }; 18 | 19 | struct variable_stack_node { 20 | char name[MAX_DATA_SIZE]; 21 | char data[MAX_DATA_SIZE]; 22 | struct variable_stack_node *next; 23 | }; 24 | 25 | typedef struct stack_node stack_node_t; 26 | typedef struct variable_stack_node variable_stack_node_t; 27 | 28 | extern stack_node_t *root; 29 | extern stack_node_t *current_stack; 30 | extern variable_stack_node_t *root_var; 31 | extern variable_stack_node_t *current_var_stack; 32 | 33 | extern bool in_block; 34 | extern bool monochrome_mode; 35 | extern bool hide_debugging; 36 | 37 | void stack_node_initialize(struct stack_node *s); 38 | void parse_line(char *line, int line_len, int linenum); 39 | void stack_execute(); 40 | void parse_cleanup(); 41 | stack_node_t * stack_search_label(char *label); 42 | variable_stack_node_t * var_stack_search_label(char *label); 43 | void set_block_execute(stack_node_t *start, bool val); 44 | bool eval_conditional(char *cond); 45 | void exit_with_error(char *error); 46 | char * get_data_for_var(char *var_name); 47 | void prepare_var_string(char *str, int32_t num_vars); 48 | char * parse_var_string(char *str); 49 | void printANSIescape(char *code); 50 | -------------------------------------------------------------------------------- /include/stringhelpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void shift_string_left(char *str, int32_t start, int32_t n); 6 | int32_t get_char_occurances(char *str, char *c); 7 | int32_t split_string_delimiter(char *buf, char *str, char *delim); 8 | int32_t split_string_delimiter_rev(char *buf, char *str, char *delim); 9 | int32_t str_index_of(char *str, char *c); 10 | int32_t str_index_of_n(char *str, char *c, int32_t n); 11 | int32_t str_index_of_skip(char *str, char *c, int32_t skip); 12 | int32_t find_option(int argc, char **argv, char *request, int32_t *counter); 13 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/sh 2 | CC=gcc 3 | CFLAGS=-std=c99 4 | PREFIX=/usr/local 5 | SRCDIR=src 6 | INCLUDEDIR=include 7 | OUTDIR=out 8 | MANDIR=doc 9 | DEPS=$(SRCDIR)/stringhelpers.o $(SRCDIR)/cmd.o 10 | 11 | include $(SRCDIR)/libbasilc/make.config 12 | 13 | .PHONY: all 14 | all: pre-build BasilC 15 | 16 | pre-build: 17 | if [ ! -d out ]; then mkdir out; fi 18 | 19 | BasilC: $(DEPS) 20 | $(CC) -o $(OUTDIR)/basilc $(DEPS) $(SRCDIR)/main.c $(CFLAGS) -I$(INCLUDEDIR) 21 | 22 | %.o: %.c 23 | $(CC) -o $@ -c $< $(CFLAGS) -I$(INCLUDEDIR) 24 | 25 | .PHONY: clean 26 | clean: 27 | rm -rf out/ $(DEPS) 28 | 29 | .PHONY: install 30 | install: BasilC 31 | mkdir -p $(DESTDIR)$(PREFIX)/bin 32 | mkdir -p $(DESTDIR)$(PREFIX)/man/man1 33 | cp $(OUTDIR)/basilc $(DESTDIR)$(PREFIX)/bin/basilc 34 | cp $(MANDIR)/basilc.1 $(DESTDIR)$(PREFIX)/man/man1/basilc.1 35 | @echo Finished Installing! 36 | 37 | .PHONY: uninstall 38 | uninstall: 39 | rm -f $(DESTDIR)$(PREFIX)/bin/basilc 40 | rm -f $(DESTDIR)$(PREFIX)/man/man1/basilc.1 41 | @echo Finished Uninstalling! 42 | -------------------------------------------------------------------------------- /src/cmd.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The BasilC Interpreter 3 | * Copyright (C) Shawn Anastasio 2016 4 | * Licensed under the GNU GPL v3 5 | */ 6 | /** 7 | * This file contains code that defines interfaces for setting and declaring 8 | * language behavior. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | registered_cmd_stack_t *root_cmd; 20 | registered_cmd_stack_t *current_cmd_stack; 21 | 22 | char parse_error_msgs[][32] = { 23 | "Success", 24 | "Missing parenthesis", 25 | "Invalid command", 26 | "Incorrect arguments", 27 | "Failed special parse" 28 | }; 29 | 30 | /** 31 | * Initalize the command stack 32 | */ 33 | void init_cmd_stack() { 34 | root_cmd = (registered_cmd_stack_t *) malloc(sizeof(registered_cmd_stack_t)); 35 | current_cmd_stack = root_cmd; 36 | } 37 | 38 | /** 39 | * Register an interpreter-recognized command 40 | */ 41 | void register_cmd(cmd_declaration_t *dec) { 42 | // Copy provided data to internal command stack 43 | current_cmd_stack->name = dec->name; 44 | current_cmd_stack->num_args = dec->num_args; 45 | current_cmd_stack->handle_cmd = dec->handle_cmd; 46 | current_cmd_stack->special_parse = dec->special_parse; 47 | 48 | // Extend stack 49 | current_cmd_stack->next = (registered_cmd_stack_t *) 50 | malloc(sizeof(registered_cmd_stack_t)); 51 | current_cmd_stack = current_cmd_stack->next; 52 | } 53 | 54 | /** 55 | * Search for a command in the registered command stack 56 | * @param label name of command 57 | * @return pointer to cmd stack node with name, or null if name isn't found 58 | */ 59 | registered_cmd_stack_t * cmd_stack_search_label(char *label) { 60 | registered_cmd_stack_t *cur = root_cmd; 61 | while (cur != NULL) { 62 | if (cur->name == NULL) break; 63 | 64 | if (strcmp(cur->name, label) == 0) { 65 | return cur; 66 | } 67 | cur = cur->next; 68 | } 69 | 70 | return NULL; 71 | } 72 | 73 | /** 74 | * Parse a user-inputted line and add to general stack if applicable 75 | */ 76 | int32_t parse_user_command(char *input, int32_t input_len) { 77 | // Skip empty strings 78 | if (input_len == 0) return ERR_SUCCESS; 79 | 80 | // Skip shebang lines 81 | if (input[0] == '#') return ERR_SUCCESS; 82 | 83 | // Skip comments 84 | if (input_len >= 3 && strncmp(input, "#//", 3) == 0) { 85 | return ERR_SUCCESS; 86 | } else if (input_len >= 9 && strncmp(input, "BasilC#//", 9) == 0) { 87 | return ERR_SUCCESS; 88 | } 89 | 90 | // Determine whether command is prefixed with BasilC- 91 | bool has_prefix = false; 92 | if (strlen(input) > 6 && strncmp(input, "BasilC-", 7) == 0) { 93 | has_prefix = true; 94 | } 95 | 96 | // Search for command in registered command stack 97 | char cmd_name[strlen(input)]; 98 | // Find first parenthesis 99 | int32_t paren = str_index_of(input, "("); 100 | if (paren == -1) { 101 | return ERR_PAREN; 102 | } 103 | 104 | // Extract command name 105 | if (has_prefix) { 106 | strncpy(cmd_name, input+7, paren-7); 107 | cmd_name[paren-7] = '\0'; 108 | } else { 109 | strncpy(cmd_name, input, paren); 110 | cmd_name[paren] = '\0'; 111 | } 112 | 113 | // Search for command in registered command stack 114 | registered_cmd_stack_t *res = cmd_stack_search_label(cmd_name); 115 | if (res == NULL) { 116 | return ERR_INVALID_CMD; 117 | } 118 | 119 | // Confirm number of given arguments with expected number 120 | int32_t num_args = get_char_occurances(input, ","); 121 | if (num_args != 0) { 122 | num_args++; 123 | } else if (input_len > paren && input[paren+1] != ')') { 124 | num_args++; 125 | } 126 | if (res->num_args >= 0 && num_args != res->num_args) { 127 | return ERR_ARGS; 128 | } 129 | 130 | // Extract arguments if applicable and add to general stack 131 | if (res->num_args > 1) { 132 | int32_t num_commas = get_char_occurances(input, ","); 133 | int32_t paren_end = str_index_of_skip(input, ")", paren); 134 | 135 | // Extract first argument and put into stack 136 | strncpy(current_stack->parameters[0], input+paren+1, str_index_of(input, ",")-paren-1); 137 | 138 | // Put in the rest 139 | int32_t i; 140 | for (i=0; iparameters[i+1], input+start+1, end-start); 147 | } 148 | 149 | current_stack->command = res->name; 150 | 151 | goto advance_stack; 152 | } else if (res->num_args == 1 || res->num_args == -1) { 153 | // Only one argument provided, or -1 was specified which forces 1 arg 154 | int32_t paren_end = str_index_of_skip(input, ")", paren); 155 | strncpy(current_stack->parameters[0], 156 | input+paren+1, paren_end-paren-1); 157 | current_stack->command = res->name; 158 | 159 | goto advance_stack; 160 | } else { 161 | // No arguments, add to stack 162 | current_stack->command = res->name; 163 | goto advance_stack; 164 | } 165 | 166 | return false; 167 | advance_stack: 168 | current_stack->execute = !in_block; 169 | // Handle special parsing commands 170 | if (res->special_parse != NULL && !(res->special_parse())) { 171 | return ERR_SPECIAL_PARSE; 172 | } 173 | current_stack->next = malloc(sizeof(stack_node_t)); 174 | stack_node_initialize(current_stack->next); 175 | current_stack = current_stack->next; 176 | return ERR_SUCCESS; 177 | } 178 | 179 | /** 180 | * Execute a command from the general stack 181 | */ 182 | bool execute_command(stack_node_t **node) { 183 | bool result; 184 | 185 | // Handle empty commands 186 | if ((*node)->command == NULL) goto skip_node; 187 | 188 | // Handle nodes marked as "don't execute" 189 | if ((*node)->execute == false) goto skip_node; 190 | 191 | // Get registered_cmd_stack entry 192 | registered_cmd_stack_t *res = cmd_stack_search_label((*node)->command); 193 | if (res == NULL) return false; 194 | 195 | // Save stack node state before calling 196 | stack_node_t *temp = *node; 197 | 198 | // Skip functions without a handler command 199 | if (res->handle_cmd == NULL) { 200 | result = true; 201 | goto increment_stack_node; 202 | } 203 | 204 | // Call handler function 205 | result = res->handle_cmd(node); 206 | 207 | increment_stack_node: 208 | // If stack wasn't modified by function, increment it to the next one 209 | if (result && *node == temp) 210 | *node = (*node)->next; 211 | 212 | return result; 213 | skip_node: 214 | *node = (*node)->next; 215 | return true; 216 | } 217 | -------------------------------------------------------------------------------- /src/libbasilc/controlflow.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The BasilC Interpreter 3 | * Copyright (C) Shawn Anastasio 2016 4 | * Licensed under the GNU GPL v3 5 | */ 6 | /** 7 | * This file contains code that defines BasilC commands related to program 8 | * control flow, such as if(), endif(), label(), goto(), and end() 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | // Handle execution of BasilC-if() 19 | bool basilc_if_callback(stack_node_t **node) { 20 | bool cond; 21 | 22 | // Try to parse variables in string 23 | char *parsed = parse_var_string((*node)->parameters[0]); 24 | if (parsed != NULL) { 25 | // If variables were parsed correctly 26 | cond = eval_conditional(parsed); 27 | free(parsed); 28 | } else { 29 | cond = eval_conditional((*node)->parameters[0]); 30 | } 31 | 32 | // If condition is true, mark all commands in block as execute 33 | if (cond) { 34 | set_block_execute(*node, true); 35 | } 36 | return true; 37 | } 38 | // Handle special parsing of BasilC-if() 39 | bool basilc_if_special_parse() { 40 | // BasilC-if() requires in_block to be set to true 41 | // at time of parsing 42 | in_block = true; 43 | return true; 44 | } 45 | 46 | // Handle special parsing of BasilC-endif() 47 | bool basilc_endif_special_parse() { 48 | if (!in_block) { 49 | return false; 50 | } else { 51 | in_block = false; 52 | return true; 53 | } 54 | } 55 | 56 | // Handle execution of BasilC-goto() 57 | bool basilc_goto_callback(stack_node_t **node) { 58 | stack_node_t *label = stack_search_label((*node)->parameters[0]); 59 | if (label != NULL) { 60 | *node = label; 61 | return true; 62 | } else { 63 | return false; 64 | } 65 | } 66 | 67 | // Handle execution of BasilC-end() 68 | bool basilc_end_callback(stack_node_t **node) { 69 | // Exit program 70 | exit(0); 71 | 72 | return false; 73 | } 74 | -------------------------------------------------------------------------------- /src/libbasilc/io.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The BasilC Interpreter 3 | * Copyright (C) Shawn Anastasio 2016 4 | * Licensed under the GNU GPL v3 5 | */ 6 | /** 7 | * This file contains code that defines BasilC commands related to input and 8 | * output operations, such as say(), tint(), and ask() 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | // Handle execution of BasilC-say() 22 | bool basilc_say_callback(stack_node_t **node) { 23 | char *parsed = parse_var_string((*node)->parameters[0]); 24 | if (parsed != NULL) { 25 | printf("%s", parsed); 26 | free(parsed); 27 | } else { 28 | printf("%s", (*node)->parameters[0]); 29 | } 30 | return true; 31 | } 32 | 33 | // Handle execution of BasilC-sayln() 34 | bool basilc_sayln_callback(stack_node_t **node) { 35 | basilc_say_callback(node); 36 | printf("\n"); 37 | return true; 38 | } 39 | 40 | //shared code used by tint() and tintbg() 41 | void basilc_handle_tint(char code, char *temp){ 42 | /* prints ANSI escape code to allow the following BasilC-say 43 | statement to be in the corresponding color */ 44 | if (monochrome_mode) 45 | return; 46 | if (strcmp(temp, "black") == 0) 47 | printf("\033[%c0m", code); 48 | else if (strcmp(temp, "red") == 0) 49 | printf("\033[%c1m", code); 50 | else if (strcmp(temp, "green") == 0) 51 | printf("\033[%c2m", code); 52 | else if (strcmp(temp, "yellow") == 0) 53 | printf("\033[%c3m", code); 54 | else if (strcmp(temp, "blue") == 0) 55 | printf("\033[%c4m", code); 56 | else if (strcmp(temp, "magenta") == 0) 57 | printf("\033[%c5m", code); 58 | else if (strcmp(temp, "cyan") == 0) 59 | printf("\033[%c6m", code); 60 | else if (strcmp(temp, "white") == 0) 61 | printf("\033[%c7m", code); 62 | /* if the color is not one of the available options, reset 63 | terminal to default color state */ 64 | else 65 | printANSIescape("\033[0m"); 66 | } 67 | 68 | // Handle execution of BasilC-tint() 69 | bool basilc_tint_callback(stack_node_t **node) { 70 | char *temp = (*node)->parameters[0]; 71 | int32_t i; 72 | for (i=0; temp[i]; i++){ 73 | temp[i] = tolower(temp[i]); 74 | } 75 | /* prints ANSI escape code to allow the following BasilC-say 76 | statement to be in the corresponding color */ 77 | basilc_handle_tint('3', temp); 78 | return true; 79 | } 80 | 81 | // Handle execution of BasilC-tintbg() 82 | bool basilc_tintbg_callback(stack_node_t **node) { 83 | char *temp = (*node)->parameters[0]; 84 | int32_t i; 85 | for (i=0; temp[i]; i++){ 86 | temp[i] = tolower(temp[i]); 87 | } 88 | basilc_handle_tint('4', temp); 89 | return true; 90 | } 91 | 92 | // Handle execution of BasilC-ask() 93 | bool basilc_ask_callback(stack_node_t **node) { 94 | variable_stack_node_t *temp_var = var_stack_search_label((*node)->parameters[1]); 95 | if (temp_var != NULL) { 96 | printf("%s", (*node)->parameters[0]); 97 | fgets(temp_var->data, STACK_PARAMETER_MAX_LENGTH, stdin); 98 | temp_var->data[strlen(temp_var->data)-1] = '\0'; 99 | return true; 100 | } else { 101 | printf("Variable %s has not been declared!\n", (*node)->parameters[1]); 102 | return false; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/libbasilc/libbasilc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The BasilC Interpreter 3 | * Copyright (C) Shawn Anastasio 2016 4 | * Licensed under the GNU GPL v3 5 | */ 6 | /** 7 | * This file contains functions that register libbasilc commands to the 8 | * interpreter's internal registered_cmd_stack. 9 | */ 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | /** 21 | * Registers all libbasilc functions to the registered_cmd_stack. 22 | */ 23 | void libbasilc_register() { 24 | /* Register controlflow functions */ 25 | register_cmd(&basilc_if); 26 | register_cmd(&basilc_endif); 27 | register_cmd(&basilc_label); 28 | register_cmd(&basilc_goto); 29 | register_cmd(&basilc_end); 30 | 31 | /* Register io functions */ 32 | register_cmd(&basilc_say); 33 | register_cmd(&basilc_sayln); 34 | register_cmd(&basilc_tint); 35 | register_cmd(&basilc_tintbg); 36 | register_cmd(&basilc_ask); 37 | 38 | /* Register system functions */ 39 | register_cmd(&basilc_yolo); 40 | register_cmd(&basilc_naptime); 41 | 42 | /* Register variable functions */ 43 | register_cmd(&basilc_define); 44 | } 45 | 46 | void __debug_print_cmd_stack() { 47 | registered_cmd_stack_t *cur = root_cmd; 48 | while (cur->next != NULL) { 49 | printf("cmd: %s\n", cur->name); 50 | cur = cur->next; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/libbasilc/make.config: -------------------------------------------------------------------------------- 1 | DEPS += \ 2 | $(SRCDIR)/libbasilc/controlflow.o \ 3 | $(SRCDIR)/libbasilc/io.o \ 4 | $(SRCDIR)/libbasilc/system.o \ 5 | $(SRCDIR)/libbasilc/variable.o \ 6 | $(SRCDIR)/libbasilc/libbasilc.o \ 7 | -------------------------------------------------------------------------------- /src/libbasilc/system.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The BasilC Interpreter 3 | * Copyright (C) Shawn Anastasio 2016 4 | * Licensed under the GNU GPL v3 5 | */ 6 | /** 7 | * This file contains code that defines BasilC commands related to host system 8 | * operations such as yolo() and naptime(). 9 | */ 10 | 11 | #include 12 | 13 | #ifdef __unix__ 14 | #include 15 | #elif _WIN32 16 | #include 17 | #define sleep(x) (Sleep(x*1000)) 18 | #endif 19 | 20 | #include 21 | 22 | // Handle execution of yolo() 23 | bool basilc_yolo_callback(stack_node_t **node) { 24 | system((*node)->parameters[0]); 25 | return true; 26 | } 27 | 28 | // Handle execution of naptime() 29 | bool basilc_naptime_callback(stack_node_t **node) { 30 | sleep(atoi((*node)->parameters[0])); 31 | return true; 32 | } 33 | -------------------------------------------------------------------------------- /src/libbasilc/variable.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The BasilC Interpreter 3 | * Copyright (C) Shawn Anastasio 2016 4 | * Licensed under the GNU GPL v3 5 | */ 6 | /** 7 | * This file contains code that defines BasilC commands related to variable 8 | * storage and usage, such as define() 9 | */ 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | // Handle execution of define() 17 | bool basilc_define_callback(stack_node_t **node) { 18 | char *var_name = (*node)->parameters[0]; 19 | char *var_data = (*node)->parameters[1]; 20 | 21 | // Check if variable already exists 22 | variable_stack_node_t *var = var_stack_search_label(var_name); 23 | if (var != NULL) { 24 | // Redefine variable 25 | strcpy(var->data, var_data); 26 | return true; 27 | } 28 | 29 | // Add to variable stack 30 | strcpy(current_var_stack->name, var_name); 31 | strcpy(current_var_stack->data, var_data); 32 | 33 | // Advance variable stack 34 | current_var_stack->next = (variable_stack_node_t *) 35 | malloc(sizeof(variable_stack_node_t)); 36 | current_var_stack = current_var_stack->next; 37 | 38 | return true; 39 | } 40 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The BasilC Interpreter 3 | * Copyright (C) Shawn Anastasio 2016 4 | * Licensed under the GNU GPL v3 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // Comments: BasilC#// (comment) 21 | // Print: BasilC-say() 22 | // Set Printing Color: BasilC-tint(red) 23 | // Label/Goto: BasilC-label(name), BasilC-goto(name) 24 | // Sleep: BasilC-naptime(sec) 25 | // System: BasilC-yolo(command) 26 | // If: BasilC-if(condition) 27 | // Endif: BasilC-endif() 28 | // Define variables: BasilC-define(var_name, var_data) 29 | // End Program: BasilC-end() 30 | 31 | stack_node_t *root; 32 | stack_node_t *current_stack; 33 | 34 | variable_stack_node_t *root_var; 35 | variable_stack_node_t *current_var_stack; 36 | 37 | bool in_block; 38 | bool monochrome_mode; 39 | bool hide_debugging; 40 | bool show_timer; 41 | 42 | int32_t main(int32_t argc, char **argv) { 43 | //start debug timer 44 | clock_t start_timer = clock(); 45 | 46 | // Verify arguments 47 | if (argc < 2 || argc > 5) { 48 | printf("Usage: %s [-m] [-d] \n", argv[0]); 49 | return 1; 50 | } 51 | 52 | // Set global variables 53 | in_block = false; 54 | monochrome_mode = false; 55 | hide_debugging = false; 56 | show_timer = false; 57 | 58 | // Initialize command stack 59 | init_cmd_stack(); 60 | 61 | // Register libbasilc functions 62 | libbasilc_register(); 63 | 64 | // Check parameters 65 | int32_t c; 66 | int32_t counter = 0; 67 | 68 | while ((c = find_option(argc, argv, "mdt", &counter)) != -1) 69 | switch (c) { 70 | case 'm': 71 | monochrome_mode = true; //don't output ANSI color codes 72 | break; 73 | case 'd': 74 | fclose(stderr); //don't show debugging and error info 75 | break; 76 | case 't': 77 | show_timer = true; //show elapse time 78 | break; 79 | } 80 | 81 | // Create initial stack 82 | root = (stack_node_t *) malloc(sizeof(stack_node_t)); 83 | current_stack = root; 84 | stack_node_initialize(root); 85 | 86 | // Create initial variable stack 87 | root_var = (variable_stack_node_t *) malloc(sizeof(variable_stack_node_t)); 88 | current_var_stack = root_var; 89 | 90 | // Open script file 91 | FILE *fp; 92 | fp = fopen(argv[argc-1], "r"); 93 | if (fp == NULL) { 94 | perror("Error"); 95 | return 1; 96 | } 97 | int32_t lines = 0; 98 | int32_t chars = 0; 99 | 100 | // Count lines and chars 101 | char cur_char = 0; 102 | while (!feof(fp)) { 103 | cur_char = fgetc(fp); 104 | if (cur_char == '\n') ++lines; 105 | chars++; 106 | } 107 | 108 | // Create Buffer with correct size 109 | char *buffer = 0; 110 | 111 | // Fill buffer 112 | if (fp) { 113 | fseek(fp, 0, SEEK_SET); 114 | buffer = malloc(chars+1); 115 | if (buffer) { 116 | fread (buffer, 1, chars, fp); 117 | } 118 | fclose(fp); 119 | buffer[chars] = '\0'; 120 | } 121 | 122 | // DEBUG BasilC(TM) 123 | fputs("BasilC Interpreter v1.0\n\n", stderr); 124 | 125 | // Begin parsing 126 | cur_char = 0; 127 | 128 | int32_t line_len = 0; 129 | int32_t pos = 0; 130 | int32_t i; 131 | for (i=0; icommand = NULL; 172 | s->execute = true; 173 | int32_t i, z; 174 | for (i=0; iparameters[i][z] = 0; 177 | } 178 | } 179 | s->next = NULL; 180 | } 181 | 182 | // Parse line of code 183 | void parse_line(char *line, int32_t line_len, int32_t linenum) { 184 | // Remove trailing \n 185 | if (line[--line_len] == '\n') line[line_len] = '\0'; 186 | 187 | // Pass line to parser 188 | int32_t result = parse_user_command(line, line_len); 189 | if (result != ERR_SUCCESS) { 190 | printf("Error: %s\n", parse_error_msgs[result]); 191 | } else { 192 | return; 193 | } 194 | 195 | parse_fail: 196 | fprintf(stderr, "At line %d: %s\n", linenum, line); 197 | exit(1); 198 | return; 199 | } 200 | 201 | void parse_cleanup() { 202 | // Check for unclosed if statement blocks 203 | if (in_block) { 204 | exit_with_error("Unclosed if statement!"); 205 | } 206 | } 207 | 208 | void stack_execute() { 209 | stack_node_t *cur = root; 210 | while (cur->next != NULL) { 211 | // Pass stack node to handler 212 | int32_t result = execute_command(&cur); 213 | if (result) { 214 | continue; 215 | } else { 216 | char error[80]; 217 | sprintf(error, "Failed to execute command: %s", cur->command); 218 | exit_with_error(error); 219 | } 220 | } 221 | } 222 | 223 | /** 224 | * Search for label in stack 225 | * @param label name of label 226 | * @return pointer to stack node with label, or NULL if label isn't found 227 | */ 228 | stack_node_t * stack_search_label(char *label) { 229 | stack_node_t *cur = root; 230 | while (cur != NULL) { 231 | if (cur->command == NULL) break; 232 | 233 | if (strcmp(cur->command, "label") == 0) { 234 | char *temp = cur->parameters[0]; 235 | if (strcmp(temp, label) == 0) { 236 | return cur; 237 | } 238 | } 239 | cur = cur->next; 240 | } 241 | 242 | return NULL; 243 | } 244 | 245 | /** 246 | * Search for variable name in variable stack 247 | * @param label name of variable 248 | * @return pointer to var stack node with name, or NULL if name isn't found 249 | */ 250 | variable_stack_node_t * var_stack_search_label(char *label) { 251 | variable_stack_node_t *cur = root_var; 252 | while (cur != NULL) { 253 | if (cur->name == NULL) break; 254 | 255 | if (strcmp(cur->name, label) == 0) { 256 | return cur; 257 | } 258 | cur = cur->next; 259 | } 260 | 261 | return NULL; 262 | } 263 | 264 | void set_block_execute(stack_node_t *cur, bool val) { 265 | while (cur != NULL) { 266 | if (cur->command == NULL) break; 267 | if (strncmp(cur->command, "endif", 5) == 0) break; 268 | 269 | cur->execute = val; 270 | cur = cur->next; 271 | } 272 | } 273 | 274 | bool eval_conditional(char *cond) { 275 | // Determine sign 276 | char sign = '\0'; 277 | if (str_index_of(cond, "=") > -1) { 278 | sign = '='; 279 | } else if (str_index_of(cond, ">") > -1) { 280 | sign = '>'; 281 | } else if (str_index_of(cond, "<") > -1) { 282 | sign = '<'; 283 | } else { 284 | exit_with_error("Invalid conditional!"); 285 | } 286 | 287 | // Determine parameters 288 | char param_1[strlen(cond)]; 289 | char param_2[strlen(cond)]; 290 | if (split_string_delimiter(param_1, cond, " ") <= -1) { 291 | exit_with_error("Invalid conditional!"); 292 | } else if (split_string_delimiter_rev(param_2, cond, " ") <= -1) { 293 | exit_with_error("Invalid conditional!"); 294 | } 295 | 296 | // Evaluate 297 | if (sign == '=') { 298 | if (atoi(param_1) == atoi(param_2)) return true; 299 | return false; 300 | } else if (sign == '>') { 301 | if (atoi(param_1) > atoi(param_2)) return true; 302 | return false; 303 | } else if (sign == '<') { 304 | if (atoi(param_1) < atoi(param_2)) return true; 305 | return false; 306 | } else { 307 | exit_with_error("Invalid conditional!"); 308 | } 309 | } 310 | 311 | void exit_with_error(char *error) { 312 | fprintf(stderr, "[error] %s\n", error); 313 | exit(1); 314 | } 315 | 316 | char * get_data_for_var(char *var_name) { 317 | variable_stack_node_t *cur = root_var; 318 | while (cur != NULL) { 319 | if (strcmp(cur->name, var_name) == 0) { 320 | return cur->data; 321 | } 322 | cur = cur->next; 323 | } 324 | return NULL; 325 | } 326 | 327 | /** 328 | * Translates all variables prefixed with $ to "%s" to be parsed later on with 329 | * sprintf. 330 | */ 331 | void prepare_var_string(char *str, int32_t num_vars) { 332 | int32_t i; 333 | for (i=0; i 12 | #include 13 | 14 | #include 15 | 16 | /** 17 | * Shift a string `n` times to the left starting at index `start` 18 | */ 19 | void shift_string_left(char *str, int32_t start, int32_t n) { 20 | int32_t i, j; 21 | // Shift entire string left by one space 'n' times 22 | for (i=0; i