diff --git a/ps5/.gitignore b/ps5/.gitignore new file mode 100644 index 0000000..b60723f --- /dev/null +++ b/ps5/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +.idea +*.log +tmp/ +.cache/ + +build/ + +*.ast +*.svg +*.symbols +*.S +*.out +!vsl_programs/*/expected/* diff --git a/ps5/CMakeLists.txt b/ps5/CMakeLists.txt new file mode 100644 index 0000000..1949c95 --- /dev/null +++ b/ps5/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.21) + +project(vslc VERSION 1.0 LANGUAGES C) + +set(VSLC_SOURCES "src/vslc.c" + "src/tree.c" + "src/graphviz_output.c" + "src/symbols.c" + "src/symbol_table.c" + "src/generator.c") + +set(VSLC_LEXER_SOURCE "src/scanner.l") +set(VSLC_PARSER_SOURCE "src/parser.y") + + +# === Setup generation of parser and scanner .c files and support headers +find_package(FLEX 2.6 REQUIRED) +find_package(BISON 3.5 REQUIRED) + +# It is highly recommended to have bison v. 3.8 or later +# This version added the very useful counterexample-feature +if(BISON_VERSION VERSION_GREATER_EQUAL 3.8) + set(BISON_FLAGS -Wcounterexamples) +endif() + +set(GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}") +set(SCANNER_GEN_C "${GEN_DIR}/scanner.c") +set(PARSER_GEN_C "${GEN_DIR}/parser.c") + +flex_target(scanner "${VSLC_LEXER_SOURCE}" "${SCANNER_GEN_C}" DEFINES_FILE "${GEN_DIR}/scanner.h") +bison_target(parser "${VSLC_PARSER_SOURCE}" "${PARSER_GEN_C}" DEFINES_FILE "${GEN_DIR}/parser.h" + COMPILE_FLAGS ${BISON_FLAGS}) +add_flex_bison_dependency(scanner parser) + + +# === Finally declare the compiler target, depending on all .c files in the project === +add_executable(vslc "${VSLC_SOURCES}" "${SCANNER_GEN_C}" "${PARSER_GEN_C}") +# Set some flags specifically for flex/bison +target_include_directories(vslc PRIVATE src "${GEN_DIR}") +target_compile_definitions(vslc PRIVATE "YYSTYPE=node_t *") +# Set general compiler flags, such as getting strdup from posix +target_compile_options(vslc PRIVATE -std=c17 -D_POSIX_C_SOURCE=200809L -Wall -g) + + +# === If Address Sanitizer is enabled, add the compiler and linker flag === + +# Enable ASan by invoking: +# cmake -B build -DUSE_ADDRESS_SANITIZER=ON +set (USE_ADDRESS_SANITIZER OFF CACHE BOOL "Should the Address Sanitizer tool be enabled?") +if (USE_ADDRESS_SANITIZER) + target_compile_options(vslc PRIVATE -fsanitize=address) + target_link_options(vslc PRIVATE -fsanitize=address) +endif() diff --git a/ps5/src/emit.h b/ps5/src/emit.h new file mode 100644 index 0000000..f096467 --- /dev/null +++ b/ps5/src/emit.h @@ -0,0 +1,94 @@ +#ifndef EMIT_H_ +#define EMIT_H_ + +#define RAX "%rax" +#define EAX "%eax" // lowest 32 bits of %rax +#define AL "%al" // lowest byte of %rax +#define RBX "%rbx" // callee saved +#define RCX "%rcx" +#define RDX "%rdx" +#define RSP "%rsp" // callee saved +#define RBP "%rbp" // callee saved +#define RSI "%rsi" +#define RDI "%rdi" +#define R8 "%r8" +#define R9 "%r9" +#define R10 "%r10" +#define R11 "%r11" +#define R12 "%r12" // callee saved +#define R13 "%r13" // callee saved +#define R14 "%r14" // callee saved +#define R15 "%r15" // callee saved +#define RIP "%rip" + +#define MEM(reg) "(" reg ")" +#define ARRAY_MEM(array, index, stride) "(" array "," index "," stride ")" + +#define DIRECTIVE(fmt, ...) printf(fmt "\n" __VA_OPT__(, ) __VA_ARGS__) +#define LABEL(name, ...) printf(name ":\n" __VA_OPT__(, ) __VA_ARGS__) +#define EMIT(fmt, ...) printf("\t" fmt "\n" __VA_OPT__(, ) __VA_ARGS__) + +#define MOVQ(src, dst) EMIT("movq %s, %s", (src), (dst)) +#define PUSHQ(src) EMIT("pushq %s", (src)) +#define POPQ(src) EMIT("popq %s", (src)) + +#define ADDQ(src, dst) EMIT("addq %s, %s", (src), (dst)) +#define SUBQ(src, dst) EMIT("subq %s, %s", (src), (dst)) +#define NEGQ(reg) EMIT("negq %s", (reg)) + +#define IMULQ(src, dst) EMIT("imulq %s, %s", (src), (dst)) +#define CQO EMIT("cqo"); // Sign extend RAX -> RDX:RAX +#define IDIVQ(by) EMIT("idivq %s", (by)) // Divide RDX:RAX by "by", store result in RAX + +#define RET EMIT("ret") + +#define CMPQ(op1, op2) EMIT("cmpq %s, %s", (op1), (op2)) // Compare the two operands + +// The SETcc-family of instructions assigns either 0 or 1 to a byte register, based on a comparison. +// The instruction immediately before the SETcc should be a +// cmpq op1, op2 +// The suffix given to SET, the "cc" part of "setcc", is the "condition code". +// It determines the kind of comparison being done. +// If the comparison is true, 1 is stored into "byte_reg". Otherwise 0 is stored. +#define SETE(byte_reg) EMIT("sete %s", (byte_reg)) // Store result of op1 == op2 +#define SETNE(byte_reg) EMIT("setne %s", (byte_reg)) // Store result of op1 != op2 +// NOTE: for inequality checks, the order of CMPQ's operands is the opposite of what you expect +// The following inequalities are all for signed integer operands +#define SETG(byte_reg) EMIT("setg %s", (byte_reg)) // Store result of op2 > op1 +#define SETGE(byte_reg) EMIT("setge %s", (byte_reg)) // Store result of op2 >= op1 +#define SETL(byte_reg) EMIT("setl %s", (byte_reg)) // Store result of op2 < op1 +#define SETLE(byte_reg) EMIT("setle %s", (byte_reg)) // Store result of op2 <= op1 + +// Since set*-instructions assign to a byte register, we must extend the byte to fill +// an entire 64-bit register, using movzbq (move Zero-extend Byte to Quadword). +#define MOVZBQ(byte_reg, full_reg) \ + EMIT("movzbq %s, %s", (byte_reg), (full_reg)) // full_reg <- byte_reg + +#define JNE(label) EMIT("jne %s", (label)) // Conditional jump (not equal) +#define JMP(label) EMIT("jmp %s", (label)) // Unconditional jump + +// Bitwise and +#define ANDQ(src, dst) EMIT("andq %s, %s", (src), (dst)) + +// These directives are set based on platform, +// allowing the compiler to work on macOS as well. +// Section names are different, +// and exported and imported function labels start with _ +#ifdef __APPLE__ +#define ASM_BSS_SECTION "__DATA, __bss" +#define ASM_STRING_SECTION "__TEXT, __cstring" +#define ASM_DECLARE_SYMBOLS \ + ".set printf, _printf \n" \ + ".set putchar, _putchar \n" \ + ".set puts, _puts \n" \ + ".set strtol, _strtol \n" \ + ".set exit, _exit \n" \ + ".set _main, main \n" \ + ".global _main" +#else +#define ASM_BSS_SECTION ".bss" +#define ASM_STRING_SECTION ".rodata" +#define ASM_DECLARE_SYMBOLS ".global main" +#endif + +#endif // EMIT_H_ diff --git a/ps5/src/generator.c b/ps5/src/generator.c new file mode 100644 index 0000000..5989a19 --- /dev/null +++ b/ps5/src/generator.c @@ -0,0 +1,225 @@ +#include "vslc.h" + +// This header defines a bunch of macros we can use to emit assembly to stdout +#include "emit.h" + +// In the System V calling convention, the first 6 integer parameters are passed in registers +#define NUM_REGISTER_PARAMS 6 +static const char* REGISTER_PARAMS[6] = {RDI, RSI, RDX, RCX, R8, R9}; + +// Takes in a symbol of type SYMBOL_FUNCTION, and returns how many parameters the function takes +#define FUNC_PARAM_COUNT(func) ((func)->node->children[1]->n_children) + +static void generate_stringtable(void); +static void generate_global_variables(void); +static void generate_function(symbol_t* function); +static void generate_expression(node_t* expression); +static void generate_statement(node_t* node); +static void generate_main(symbol_t* first); + +// Entry point for code generation +void generate_program(void) +{ + generate_stringtable(); + generate_global_variables(); + + // This directive announces that the following assembly belongs to the .text section, + // which is the section where all executable assembly lives + DIRECTIVE(".text"); + + // TODO (Task 3): + // For each function in global_symbols, generate it using generate_function () + + // In VSL, the topmost function in a program is its entry point. + // We want to be able to take parameters from the command line, + // and have them be sent into the entry point function. + // + // Due to the fact that parameters are all passed as strings, + // and passed as the (argc, argv)-pair, we need to make a wrapper for our entry function. + // This wrapper handles string -> int64_t conversion, and is already implemented. + // call generate_main ( ); +} + +// Prints one .asciz entry for each string in the global string_list +static void generate_stringtable(void) +{ + // This section is where read-only string data is stored + // It is called .rodata on Linux, and "__TEXT, __cstring" on macOS + DIRECTIVE(".section %s", ASM_STRING_SECTION); + + // These strings are used by printf + DIRECTIVE("intout: .asciz \"%s\"", "%ld"); + DIRECTIVE("strout: .asciz \"%s\"", "%s"); + // This string is used by the entry point-wrapper + DIRECTIVE("errout: .asciz \"%s\"", "Wrong number of arguments"); + + // TODO (Task 1): Print all strings in the program here, with labels you can refer to later + // You have access to the global variables string_list and string_list_len from symbols.c +} + +// Prints .zero entries in the .bss section to allocate room for global variables and arrays +static void generate_global_variables(void) +{ + // This section is where zero-initialized global variables lives + // It is called .bss on linux, and "__DATA, __bss" on macOS + DIRECTIVE(".section %s", ASM_BSS_SECTION); + DIRECTIVE(".align 8"); + + // TODO (Task 2): Fill this section with all global variables and global arrays + // Give each a label you can find later, and the appropriate size. + // Regular variables are 8 bytes, while arrays are 8 bytes per element. + // Remember to mangle the name in some way, to avoid collisions with labels + // (for example, put a '.' in front of the symbol name) + + // As an example, to set aside 16 bytes and label it .myBytes, write: + // DIRECTIVE(".myBytes: .zero 16") +} + +// Global variable used to make the functon currently being generated accessible from anywhere +static symbol_t* current_function; + +// Prints the entry point. preamble, statements and epilouge of the given function +static void generate_function(symbol_t* function) +{ + // TODO (Task 3) + + // TODO (Task 3.1): Do the prologue, including call frame building and parameter pushing + // Tip: use the definitions REGISTER_PARAMS and NUM_REGISTER_PARAMS at the top of this file + + // TODO (Task 4): the function body can be sent to generate_statement() + + // TODO (Task 3.2): Emit the epilogue, including a label and a default return value (0) +} + +// Generates code for a function call, which can either be a statement or an expression +static void generate_function_call(node_t* call) +{ + // TODO (Task 4.3) +} + +// Generates code to evaluate the expression, and place the result in %rax +static void generate_expression(node_t* expression) +{ + // TODO (Task 4.1): Generate code for evaluating the given expression. + // (The candidates are NUMBER_LITERAL, IDENTIFIER, ARRAY_INDEXING, OPERATOR and FUNCTION_CALL) +} + +static void generate_assignment_statement(node_t* statement) +{ + // TODO (Task 4.2): + // You can assign to both local variables, global variables and function parameters. + // Use the IDENTIFIER's symbol to find out what kind of symbol you are assigning to. + // The left hand side of an assignment statement may also be an ARRAY_INDEXING node. + // In that case, you must also emit code for evaluating the index being stored to +} + +static void generate_print_statement(node_t* statement) +{ + // TODO (Task 4.4): + // Remember to call safe_printf instead of printf +} + +static void generate_return_statement(node_t* statement) +{ + // TODO (Task 4.5): Evaluate the return value, store it in %rax and jump to the function epilogue +} + +// Recursively generate the given statement node, and all sub-statements. +static void generate_statement(node_t* node) +{ + if (node == NULL) + return; + + // TODO (Task 4): Generate instructions for statements. + // The statements you must handle are BLOCK, ASSIGNMENT_STATEMENT, + // PRINT_STATEMENT, RETURN_STATEMENT and FUNCTION_CALL. + // Statements of type LOCAL_VARIABLE should be ignored. +} + +static void generate_safe_printf(void) +{ + LABEL("safe_printf"); + + PUSHQ(RBP); + MOVQ(RSP, RBP); + // This is a bitmask that abuses how negative numbers work, to clear the last 4 bits + // A stack pointer that is not 16-byte aligned, will be moved down to a 16-byte boundary + ANDQ("$-16", RSP); + EMIT("call printf"); + // Cleanup the stack back to how it was + MOVQ(RBP, RSP); + POPQ(RBP); + RET; +} + +// Generates the scaffolding for parsing integers from the command line, and passing them to the +// entry point of the VSL program. The VSL entry function is specified using the parameter "first". +static void generate_main(symbol_t* first) +{ + // Make the globally available main function + LABEL("main"); + + // Save old base pointer, and set new base pointer + PUSHQ(RBP); + MOVQ(RSP, RBP); + + // Which registers argc and argv are passed in + const char* argc = RDI; + const char* argv = RSI; + + const size_t expected_args = FUNC_PARAM_COUNT(first); + + SUBQ("$1", argc); // argc counts the name of the binary, so subtract that + EMIT("cmpq $%ld, %s", expected_args, argc); + JNE("ABORT"); // If the provdied number of arguments is not equal, go to the abort label + + if (expected_args == 0) + goto skip_args; // No need to parse argv + + // Now we emit a loop to parse all parameters, and push them to the stack, + // in right-to-left order + + // First move the argv pointer to the vert rightmost parameter + EMIT("addq $%ld, %s", expected_args * 8, argv); + + // We use rcx as a counter, starting at the number of arguments + MOVQ(argc, RCX); + LABEL("PARSE_ARGV"); // A loop to parse all parameters + PUSHQ(argv); // push registers to caller save them + PUSHQ(RCX); + + // Now call strtol to parse the argument + EMIT("movq (%s), %s", argv, RDI); // 1st argument, the char * + MOVQ("$0", RSI); // 2nd argument, a null pointer + MOVQ("$10", RDX); // 3rd argument, we want base 10 + EMIT("call strtol"); + + // Restore caller saved registers + POPQ(RCX); + POPQ(argv); + PUSHQ(RAX); // Store the parsed argument on the stack + + SUBQ("$8", argv); // Point to the previous char* + EMIT("loop PARSE_ARGV"); // Loop uses RCX as a counter automatically + + // Now, pop up to 6 arguments into registers instead of stack + for (size_t i = 0; i < expected_args && i < NUM_REGISTER_PARAMS; i++) + POPQ(REGISTER_PARAMS[i]); + +skip_args: + + EMIT("call .%s", first->name); + MOVQ(RAX, RDI); // Move the return value of the function into RDI + EMIT("call exit"); // Exit with the return value as exit code + + LABEL("ABORT"); // In case of incorrect number of arguments + EMIT("leaq errout(%s), %s", RIP, RDI); + EMIT("call puts"); // print the errout string + MOVQ("$1", RDI); + EMIT("call exit"); // Exit with return code 1 + + generate_safe_printf(); + + // Declares global symbols we use or emit, such as main and printf + DIRECTIVE("%s", ASM_DECLARE_SYMBOLS); +} diff --git a/ps5/src/graphviz_output.c b/ps5/src/graphviz_output.c new file mode 100644 index 0000000..3794b13 --- /dev/null +++ b/ps5/src/graphviz_output.c @@ -0,0 +1,71 @@ +#include "vslc.h" + +// Helper function for escaping special characters when printing GraphViz strings +static void print_escaped_string(char* str) +{ + for (char* c = str; *c != '\0'; c++) + { + switch (*c) + { + case '\\': + printf("\\\\"); + break; + case '"': + printf("\\\""); + break; + case '\n': + printf("\\\\n"); + break; + default: + putchar(*c); + break; + } + } +} + +// A recursive function for printing a node as GraphViz, and all its children +static void graphviz_node_print_internal(node_t* node) +{ + printf("node%p [label=\"%s", node, NODE_TYPE_NAMES[node->type]); + switch (node->type) + { + case OPERATOR: + printf("\\n%s", node->data.operator); + break; + case IDENTIFIER: + printf("\\n%s", node->data.identifier); + break; + case NUMBER_LITERAL: + printf("\\n%ld", node->data.number_literal); + break; + case STRING_LITERAL: + printf("\\n"); + print_escaped_string(node->data.string_literal); + break; + case STRING_LIST_REFERENCE: + printf("\\n%zu", node->data.string_list_index); + break; + default: + break; + } + + printf("\"];\n"); + for (size_t i = 0; i < node->n_children; i++) + { + node_t* child = node->children[i]; + if (child == NULL) + printf("node%p -- node%pNULL%zu ;\n", node, node, i); + else + { + printf("node%p -- node%p ;\n", node, child); + graphviz_node_print_internal(child); + } + } +} + +void graphviz_node_print(node_t* root) +{ + printf("graph \"\" {\n node[shape=box];\n"); + graphviz_node_print_internal(root); + printf("}\n"); +} diff --git a/ps5/src/nodetypes.h b/ps5/src/nodetypes.h new file mode 100644 index 0000000..9faf7ac --- /dev/null +++ b/ps5/src/nodetypes.h @@ -0,0 +1,33 @@ +// This is a special file that is not intended to be #include-d normally. +// Instead, it is included by "tree.h" and "tree.c" to provide both an enum of node types, +// and an array of strings containing the node names. + +// clang-format off + +#ifndef NODE_TYPE +#error The file nodetypes.h should only be included after defining the NODE_TYPE macro +#endif + +NODE_TYPE(LIST), +NODE_TYPE(GLOBAL_VARIABLE_DECLARATION), +NODE_TYPE(ARRAY_INDEXING), +NODE_TYPE(VARIABLE), +NODE_TYPE(FUNCTION), +NODE_TYPE(BLOCK), +NODE_TYPE(LOCAL_VARIABLE_DECLARATION), +NODE_TYPE(LOCAL_VARIABLE), +NODE_TYPE(ASSIGNMENT_STATEMENT), +NODE_TYPE(RETURN_STATEMENT), +NODE_TYPE(PRINT_STATEMENT), +NODE_TYPE(PRINTLN_STATEMENT), +NODE_TYPE(IF_STATEMENT), +NODE_TYPE(WHILE_STATEMENT), +NODE_TYPE(BREAK_STATEMENT), +NODE_TYPE(FUNCTION_CALL), +NODE_TYPE(OPERATOR), // uses the data field "operator" +NODE_TYPE(IDENTIFIER), // uses and owns the data field "identifer" +NODE_TYPE(NUMBER_LITERAL), // uses the data field "number_literal" +NODE_TYPE(STRING_LITERAL), // uses and owns the data field "string_literal" +NODE_TYPE(STRING_LIST_REFERENCE), // uses the data field "string_list_index" + +#undef NODE_TYPE diff --git a/ps5/src/parser.y b/ps5/src/parser.y new file mode 100644 index 0000000..842f2cb --- /dev/null +++ b/ps5/src/parser.y @@ -0,0 +1,262 @@ +%{ +#include "vslc.h" + +// State variables from the flex generated scanner +extern int yylineno; // The line currently being read +extern char yytext[]; // The text of the last consumed lexeme + +// The main flex driver function used by the parser +int yylex(void); + +// The function called by the parser when errors occur +int yyerror(const char *error) +{ + fprintf(stderr, "%s on line %d\n", error, yylineno); + exit(EXIT_FAILURE); +} + +// Helper macros for creating nodes +#define N0C(type) \ + node_create( (type), 0 ) +#define N1C(type, child0) \ + node_create( (type), 1, (child0) ) +#define N2C(type, child0, child1) \ + node_create( (type), 2, (child0), (child1) ) +#define N3C(type, child0, child1, child2) \ + node_create( (type), 3, (child0), (child1), (child2) ) +%} + +%token FUNC VAR RETURN PRINT PRINTLN IF ELSE WHILE BREAK AND OR +%token NUMBER_TOKEN IDENTIFIER_TOKEN STRING_TOKEN + +// Use operator precedence to ensure order of operations is correct +%left '?' ':' // The ternary ? : operator has the lowest precedence of them all +%left OR // Or has lower precedence than and, just like in C +%left AND +%left '=' '!' // == and != +%left '<' '>' // < <= > and >= +%left '+' '-' +%left '*' '/' +%right UNARY_OPERATORS + +// Resolve the nested if-if-else ambiguity with precedence +%nonassoc ')' +%nonassoc ELSE + +%% +program : + global_list { root = $1; } + ; +global_list : + global { $$ = N1C(LIST, $1); } + | global_list global { $$ = append_to_list_node($1, $2); } + ; +global : + function { $$ = $1; } + | global_variable_declaration { $$ = $1; } + ; +global_variable_declaration : + VAR global_variable_list { $$ = N1C(GLOBAL_VARIABLE_DECLARATION, $2); } + ; +global_variable_list : + global_variable { $$ = N1C(LIST, $1); } + | global_variable_list ',' global_variable { $$ = append_to_list_node($1, $3); } + ; +global_variable : + identifier { $$ = $1; } + | array_indexing { $$ = $1; } + ; +array_indexing: + identifier '[' expression ']' { $$ = N2C(ARRAY_INDEXING, $1, $3); } + ; +parameter_list : + /* epsilon */ { $$ = N0C(LIST); } + | identifier { $$ = N1C(LIST, $1); } + | parameter_list ',' identifier { $$ = append_to_list_node($1, $3); } + ; +function : + FUNC identifier '(' parameter_list ')' statement + { $$ = N3C(FUNCTION, $2, $4, $6); } + ; +statement : + block { $$ = $1; } + | assignment_statement { $$ = $1; } + | return_statement { $$ = $1; } + | print_statement { $$ = $1; } + | println_statement { $$ = $1; } + | if_statement { $$ = $1; } + | while_statement { $$ = $1; } + | break_statement { $$ = $1; } + | function_call { $$ = $1; } + ; +block : + '{' statement_or_declaration_list '}' { $$ = N1C(BLOCK, $2); } + ; +statement_or_declaration_list : + /* epsilon */ { $$ = N0C(LIST); } + | statement_or_declaration_list statement_or_declaration { $$ = append_to_list_node($1, $2); } + ; +statement_or_declaration : + statement { $$ = $1; } + | local_variable_declaration { $$ = $1; } + ; +local_variable_declaration : + VAR local_variable_list { $$ = N1C(LOCAL_VARIABLE_DECLARATION, $2); } + ; +local_variable_list : + local_variable { $$ = N1C(LIST, $1); } + | local_variable_list ',' local_variable { $$ = append_to_list_node($1, $3); } + ; +local_variable : + identifier { $$ = N1C(LOCAL_VARIABLE, $1); } + | identifier '=' expression { $$ = N2C(LOCAL_VARIABLE, $1, $3); } + ; +assignment_statement : + identifier '=' expression { $$ = N2C(ASSIGNMENT_STATEMENT, $1, $3); } + | array_indexing '=' expression { $$ = N2C(ASSIGNMENT_STATEMENT, $1, $3); } + ; +return_statement : + RETURN expression + { $$ = N1C(RETURN_STATEMENT, $2); } + ; +print_statement : + PRINT '(' print_list ')' + { $$ = N1C(PRINT_STATEMENT, $3); } + ; +println_statement : + PRINTLN '(' print_list ')' + { $$ = N1C(PRINTLN_STATEMENT, $3); } + ; +print_list : + print_item { $$ = N1C(LIST, $1); } + | print_list ',' print_item { $$ = append_to_list_node($1, $3); } + ; +print_item : + expression { $$ = $1; } + | string { $$ = $1; } + ; +break_statement : + BREAK { $$ = N0C(BREAK_STATEMENT); } + ; +if_statement : + IF '(' expression ')' statement + { $$ = N2C(IF_STATEMENT, $3, $5); } + | IF '(' expression ')' statement ELSE statement + { $$ = N3C(IF_STATEMENT, $3, $5, $7); } + ; +while_statement : + WHILE '(' expression ')' statement + { $$ = N2C(WHILE_STATEMENT, $3, $5); } + ; +expression : + expression '?' expression ':' expression + { + $$ = N3C(OPERATOR, $1, $3, $5); + $$->data.operator = "?:"; + } + | expression OR expression + { + $$ = N2C(OPERATOR, $1, $3); + $$->data.operator = "or"; + } + | expression AND expression + { + $$ = N2C(OPERATOR, $1, $3); + $$->data.operator = "and"; + } + | expression '=' '=' expression + { + $$ = N2C(OPERATOR, $1, $4); + $$->data.operator = "=="; + } + | expression '!' '=' expression + { + $$ = N2C(OPERATOR, $1, $4); + $$->data.operator = "!="; + } + | expression '<' expression + { + $$ = N2C(OPERATOR, $1, $3); + $$->data.operator = "<"; + } + | expression '<' '=' expression + { + $$ = N2C(OPERATOR, $1, $4); + $$->data.operator = "<="; + } + | expression '>' expression + { + $$ = N2C(OPERATOR, $1, $3); + $$->data.operator = ">"; + } + | expression '>' '=' expression + { + $$ = N2C(OPERATOR, $1, $4); + $$->data.operator = ">="; + } + | expression '+' expression + { + $$ = N2C(OPERATOR, $1, $3); + $$->data.operator = "+"; + } + | expression '-' expression + { + $$ = N2C(OPERATOR, $1, $3); + $$->data.operator = "-"; + } + | expression '*' expression + { + $$ = N2C(OPERATOR, $1, $3); + $$->data.operator = "*"; + } + | expression '/' expression + { + $$ = N2C(OPERATOR, $1, $3); + $$->data.operator = "/"; + } + | '-' expression %prec UNARY_OPERATORS + { + $$ = N1C(OPERATOR, $2); + $$->data.operator = "-"; + } + | '!' expression %prec UNARY_OPERATORS + { + $$ = N1C(OPERATOR, $2); + $$->data.operator = "!"; + } + | '(' expression ')' { $$ = $2; } + | number { $$ = $1; } + | identifier { $$ = $1; } + | array_indexing { $$ = $1; } + | function_call { $$ = $1; } + ; +function_call : + identifier '(' argument_list ')' { $$ = N2C(FUNCTION_CALL, $1, $3); } +argument_list : + expression_list { $$ = $1; } + | /* epsilon */ { $$ = N0C(LIST); } + ; +expression_list : + expression { $$ = N1C(LIST, $1); } + | expression_list ',' expression { $$ = append_to_list_node($1, $3); } + ; +identifier : + IDENTIFIER_TOKEN + { + $$ = N0C(IDENTIFIER); + // Allocate a copy of yytext to keep in the syntax tree as data + $$->data.identifier = strdup(yytext); + } +number : + NUMBER_TOKEN + { + $$ = N0C(NUMBER_LITERAL); + $$->data.number_literal = strtol(yytext, NULL, 10); + } +string : + STRING_TOKEN + { + $$ = N0C(STRING_LITERAL); + $$->data.string_literal = strdup(yytext); + } +%% diff --git a/ps5/src/scanner.l b/ps5/src/scanner.l new file mode 100644 index 0000000..094c2b8 --- /dev/null +++ b/ps5/src/scanner.l @@ -0,0 +1,38 @@ +%{ +#include "vslc.h" + +// The tokens defined in parser.y +#include "parser.h" + +// parser.h contains some unused functions, ignore that +#pragma GCC diagnostic ignored "-Wunused-function" +%} + +%option noyywrap +%option array +%option yylineno + +WHITESPACE [ \t\v\r\n] +COMMENT \/\/[^\n]+ +QUOTED \"([^\"\n]|\\\")*\" + +%% +{WHITESPACE}+ { /* Eliminate whitespace */ } +{COMMENT} { /* Eliminate comments */ } +func { return FUNC; } +var { return VAR; } +return { return RETURN; } +print { return PRINT; } +println { return PRINTLN; } +if { return IF; } +else { return ELSE; } +while { return WHILE; } +break { return BREAK; } +and { return AND; } +or { return OR; } +[0-9]+ { return NUMBER_TOKEN; } +[A-Za-z_][0-9A-Za-z_]* { return IDENTIFIER_TOKEN; } +{QUOTED} { return STRING_TOKEN; } + /* Unknown chars get returned as single char tokens */ +. { return yytext[0]; } +%% diff --git a/ps5/src/symbol_table.c b/ps5/src/symbol_table.c new file mode 100644 index 0000000..b84af74 --- /dev/null +++ b/ps5/src/symbol_table.c @@ -0,0 +1,168 @@ +#include "symbol_table.h" +#include "symbols.h" +#include +#include +#include + +static insert_result_t symbol_hashmap_insert(symbol_hashmap_t* hashmap, symbol_t* symbol); + +// ================== Symbol table code ================= +// Initializes a symboltable with 0 entries. Will be resized upon first insertion +symbol_table_t* symbol_table_init(void) +{ + symbol_table_t* result = malloc(sizeof(symbol_table_t)); + *result = (symbol_table_t){ + .symbols = NULL, + .n_symbols = 0, + .capacity = 0, + .hashmap = symbol_hashmap_init()}; + return result; +} + +// Adds a symbol to both the symbol table, and its hashmap (if possible) +insert_result_t symbol_table_insert(symbol_table_t* table, struct symbol* symbol) +{ + // Inserts can fail, if the hashmap already contains the name + if (symbol_hashmap_insert(table->hashmap, symbol) == INSERT_COLLISION) + return INSERT_COLLISION; + + // If the table is full, resize the list + if (table->n_symbols + 1 >= table->capacity) + { + table->capacity = table->capacity * 2 + 8; + table->symbols = realloc(table->symbols, table->capacity * sizeof(symbol_t*)); + } + + table->symbols[table->n_symbols] = symbol; + symbol->sequence_number = table->n_symbols; + table->n_symbols++; + + return INSERT_OK; +} + +// Destroys the given symbol table, its hashmap, and all the symbols it owns +void symbol_table_destroy(symbol_table_t* table) +{ + for (int i = 0; i < table->n_symbols; i++) + free(table->symbols[i]); + free(table->symbols); + symbol_hashmap_destroy(table->hashmap); + free(table); +} + +// ==================== Hashmap code ==================== + +// Initializes a hashmap with 0 buckets. Will be resized upon first insertion +symbol_hashmap_t* symbol_hashmap_init() +{ + symbol_hashmap_t* result = malloc(sizeof(symbol_hashmap_t)); + *result = (symbol_hashmap_t){.buckets = NULL, .n_buckets = 0, .n_entries = 0, .backup = NULL}; + return result; +} + +// Calculates a naive 64-bit hash of the given string +static uint64_t hash_string(const char* string) +{ + assert(string != NULL); + uint64_t hash = 31; + for (const char* c = string; *c != '\0'; c++) + hash = hash * 257 + *c; + return hash; +} + +// Allocates a larger list of buckets, and inserts all hashmap entries again +static void symbol_hashmap_resize(symbol_hashmap_t* hashmap, size_t new_capacity) +{ + symbol_t** old_buckets = hashmap->buckets; + size_t old_capacity = hashmap->n_buckets; + + // Use calloc, since it initalizes the memory to 0, aka NULL entries + hashmap->buckets = calloc(new_capacity, sizeof(symbol_t*)); + hashmap->n_buckets = new_capacity; + hashmap->n_entries = 0; + + // Now re-insert all entries from the old buckets + for (int i = 0; i < old_capacity; i++) + { + if (old_buckets[i] != NULL) + symbol_hashmap_insert(hashmap, old_buckets[i]); + } + + free(old_buckets); +} + +// Performs insertion into the hashmap. +// The hashmap uses open addressing, with up to one entry per bucket. +// If our first choice of bucket is full, we look at the next bucket, until we find room. +static insert_result_t symbol_hashmap_insert(symbol_hashmap_t* hashmap, symbol_t* symbol) +{ + // Make sure that the fill ratio of the hashmap never exeeds 1/2 + int new_size = hashmap->n_entries + 1; + if (new_size * 2 > hashmap->n_buckets) + symbol_hashmap_resize(hashmap, hashmap->n_buckets * 2 + 8); + + // Now calculate the position of the new entry + uint64_t hash = hash_string(symbol->name); + size_t bucket = hash % hashmap->n_buckets; + + // Iterate until we find an empty bucket + while (hashmap->buckets[bucket] != NULL) + { + // Check if the existing entry is a name collision + if (strcmp(hashmap->buckets[bucket]->name, symbol->name) == 0) + return INSERT_COLLISION; // An entry with the same name already exists + // Go to the next bucket + bucket = (bucket + 1) % hashmap->n_buckets; + } + + // We found an emoty bucket, insert the symbol here + hashmap->buckets[bucket] = symbol; + hashmap->n_entries++; + return INSERT_OK; // We successfully inserted a new symbol +} + +// Performs lookup in the hashmap. +// Hashes the given string, and checks if the resulting bucket contains the item. +// Since the hashmap uses open addressing, the entry can also be in the next bucket, +// so we iterate until we either find the item, or find an empty bucket. +// +// If the key isn't found in this hashmap, but we have a backup, lookup continues there. +// Otherwise, NULL is returned. +symbol_t* symbol_hashmap_lookup(symbol_hashmap_t* hashmap, const char* name) +{ + uint64_t hash = hash_string(name); + + // Loop through the linked list of hashmaps and backup hashmaps + while (hashmap != NULL) + { + // Skip any hashmaps with 0 buckets + if (hashmap->n_buckets == 0) + { + hashmap = hashmap->backup; + continue; + } + + size_t bucket = hash % hashmap->n_buckets; + while (hashmap->buckets[bucket] != NULL) + { + // Check if the entry in the bucket has a matching name + if (strcmp(hashmap->buckets[bucket]->name, name) == 0) + return hashmap->buckets[bucket]; + + // Otherwise keep iterating until we find a hit, or an empty bucket + bucket = (bucket + 1) % hashmap->n_buckets; + } + + // No entry with the required name existed in the hashmap, so go to the backup + hashmap = hashmap->backup; + } + + // The entry was never found, and we are all out of backups + return NULL; +} + +void symbol_hashmap_destroy(symbol_hashmap_t* hashmap) +{ + free(hashmap->buckets); + free(hashmap); +} diff --git a/ps5/src/symbol_table.h b/ps5/src/symbol_table.h new file mode 100644 index 0000000..48fde63 --- /dev/null +++ b/ps5/src/symbol_table.h @@ -0,0 +1,64 @@ +#ifndef SYMBOL_TABLE_H +#define SYMBOL_TABLE_H + +#include "tree.h" +#include +#include +#include + +// We use hashmaps to make lookups quick. +// The entries are symbols, using the name of the symbol as the key. +// The hashmap logic is already implemented in symbol_table.c +// NOTE that this hashmap does not support removing entries. +typedef struct symbol_hashmap +{ + struct symbol** buckets; // A bucket may contain 0 or 1 entries + size_t n_buckets; + size_t n_entries; + + // If a key is not found, the lookup function will consult this as a backup + struct symbol_hashmap* backup; +} symbol_hashmap_t; + +// A dynamically sized list of symbols, including a hashmap for fast lookups +// The logic for the symbol table is already implemented in symbol_table.c +typedef struct symbol_table +{ + struct symbol** symbols; + size_t n_symbols; + size_t capacity; + symbol_hashmap_t* hashmap; +} symbol_table_t; + +typedef enum +{ + INSERT_OK = 0, + INSERT_COLLISION = 1 +} insert_result_t; + +// Initializes a new, empty symbol table, including an empty hashmap +symbol_table_t* symbol_table_init(void); + +// Tries to insert the given symbol into the symbol table. +// If the topmost hashmap already contains a symbol with the same name, +// INSERT_COLLISION is returned, otherwise the result is INSERT_OK. +// +// The symbol table takes ownership of the symbol, and assigns it a sequence number. +// DO NOT change the symbol's name after insertion. +insert_result_t symbol_table_insert(symbol_table_t* table, struct symbol* symbol); + +// Destroys the given symbol table, its hashmap, and all the symbols it owns +void symbol_table_destroy(symbol_table_t* table); + +// Initalizes a new, empty hashmap +symbol_hashmap_t* symbol_hashmap_init(void); + +// Looks for a symbol in the symbol hashmap, matching the given name. +// If no symbol is found, the hashmap's backup hashmap is checked. +// If the name can't be found in the backup chain either, NULL is returned. +struct symbol* symbol_hashmap_lookup(symbol_hashmap_t* hashmap, const char* name); + +// Frees the memory used by the hashmap +void symbol_hashmap_destroy(symbol_hashmap_t* hashmap); + +#endif // SYMBOL_TABLE_H diff --git a/ps5/src/symbols.c b/ps5/src/symbols.c new file mode 100644 index 0000000..51d3af3 --- /dev/null +++ b/ps5/src/symbols.c @@ -0,0 +1,294 @@ +#include "vslc.h" + +// Declaration of global symbol table +symbol_table_t* global_symbols; + +// Declarations of helper functions defined further down in this file +static void find_globals(void); +static void bind_names(symbol_table_t* local_symbols, node_t* root); +static void print_symbol_table(symbol_table_t* table, int nesting); +static void destroy_symbol_tables(void); + +static size_t add_string(char* string); +static void print_string_list(void); +static void destroy_string_list(void); + +/* External interface */ + +// Creates a global symbol table, and local symbol tables for each function. +// All usages of symbols are bound to their symbol table entries. +// All strings are entered into the string_list +void create_tables(void) +{ + // Create a global symbol table, and make symbols for all globals + find_globals(); + + // For all functions, we want to fill their local symbol tables, + // and bind all names found in the function body + for (size_t i = 0; i < global_symbols->n_symbols; i++) + { + symbol_t* symbol = global_symbols->symbols[i]; + if (symbol->type == SYMBOL_FUNCTION) + bind_names(symbol->function_symtable, symbol->node->children[2]); + } +} + +// Prints the global symbol table, and the local symbol tables for each function. +// Also prints the global string list. +// Finally prints out the AST again, with bound symbols. +void print_tables(void) +{ + print_symbol_table(global_symbols, 0); + printf("\n == STRING LIST == \n"); + print_string_list(); + printf("\n == BOUND SYNTAX TREE == \n"); + print_syntax_tree(); +} + +// Cleans up all memory owned by symbol tables and the global string list +void destroy_tables(void) +{ + destroy_symbol_tables(); + destroy_string_list(); +} + +/* Internal matters */ + +#define CREATE_AND_INSERT_SYMBOL(table, ...) \ + do \ + { \ + symbol_t* symbol = malloc(sizeof(symbol_t)); \ + *symbol = (symbol_t){__VA_ARGS__}; \ + if (symbol_table_insert((table), symbol) == INSERT_COLLISION) \ + { \ + fprintf(stderr, "error: symbol '%s' already defined\n", symbol->name); \ + exit(EXIT_FAILURE); \ + } \ + } while (false) + +// Goes through all global declarations, adding them to the global symbol table. +// When adding functions, a local symbol table with symbols for its parameters are created. +static void find_globals(void) +{ + global_symbols = symbol_table_init(); + + for (size_t i = 0; i < root->n_children; i++) + { + node_t* node = root->children[i]; + if (node->type == GLOBAL_VARIABLE_DECLARATION) + { + node_t* global_variable_list = node->children[0]; + for (size_t j = 0; j < global_variable_list->n_children; j++) + { + node_t* var = global_variable_list->children[j]; + char* name; + symtype_t symtype; + + // The global variable list can both contain arrays and normal variables. + if (var->type == ARRAY_INDEXING) + { + name = var->children[0]->data.identifier; + symtype = SYMBOL_GLOBAL_ARRAY; + } + else + { + assert(var->type == IDENTIFIER); + name = var->data.identifier; + symtype = SYMBOL_GLOBAL_VAR; + } + + CREATE_AND_INSERT_SYMBOL( + global_symbols, + .name = name, + .type = symtype, + .node = var, + .function_symtable = NULL); + } + } + else if (node->type == FUNCTION) + { + // Functions have their own local symbol table. We make it now, and add the function + // parameters + symbol_table_t* function_symtable = symbol_table_init(); + // We let the global hashmap be the backup of the local scope + function_symtable->hashmap->backup = global_symbols->hashmap; + + node_t* parameters = node->children[1]; + for (int j = 0; j < parameters->n_children; j++) + { + CREATE_AND_INSERT_SYMBOL( + function_symtable, + .name = parameters->children[j]->data.identifier, + .type = SYMBOL_PARAMETER, + .node = parameters->children[j], + .function_symtable = NULL); + } + + CREATE_AND_INSERT_SYMBOL( + global_symbols, + .name = node->children[0]->data.identifier, + .type = SYMBOL_FUNCTION, + .node = node, + .function_symtable = function_symtable); + } + else + { + assert(false && "Unknown global node type"); + } + } +} + +// Creates a new empty hashmap for the symbol table, using the outer scope's hashmap as backup +static void push_local_scope(symbol_table_t* table) +{ + symbol_hashmap_t* hashmap = symbol_hashmap_init(); + hashmap->backup = table->hashmap; + table->hashmap = hashmap; +} + +// Destroys the hashmap, and replaces it with the outer scope's hashmap +static void pop_local_scope(symbol_table_t* table) +{ + symbol_hashmap_t* hashmap = table->hashmap; + table->hashmap = hashmap->backup; + symbol_hashmap_destroy(hashmap); +} + +// A recursive function that traverses the body of a function doing the following: +// - LOCAL_VARIABLE declarations have symbols created in the function's local symbol table. +// - BLOCKs push and pop lexical scopes for local variables. +// - All IDENTIFIER nodes that are not declarations are bound to the symbol it references. +// - STRING_LITERAL nodes get their data moved into the global string list. +// The node is replaced with a STRING_LIST_REFERENCE node, +// where the data.string_list_index field is set to the string's index in the string list. +static void bind_names(symbol_table_t* local_symbols, node_t* node) +{ + if (node == NULL) + return; + + switch (node->type) + { + // Local variable declarations are added to the local symbol table + case LOCAL_VARIABLE: + { + assert(node->n_children == 1); + node_t* identifier = node->children[0]; + assert(identifier->type == IDENTIFIER); + CREATE_AND_INSERT_SYMBOL( + local_symbols, + .name = identifier->data.identifier, + .type = SYMBOL_LOCAL_VAR, + .node = node, + .function_symtable = local_symbols); + + break; + } + // Can either be a variable in an expression, or the name of a function in a function call + // Either way, we wish to associate it with its symbol + case IDENTIFIER: + { + symbol_t* symbol = symbol_hashmap_lookup(local_symbols->hashmap, node->data.identifier); + if (symbol == NULL) + { + fprintf(stderr, "error: unrecognized symbol '%s'\n", (char*)node->data.identifier); + exit(EXIT_FAILURE); + } + node->symbol = symbol; + break; + } + + // Blocks push a new symbol hash map for all symbols declared inside the block + case BLOCK: + { + push_local_scope(local_symbols); + // Iterate through all declarations in the delcaration list + bind_names(local_symbols, node->children[0]); + pop_local_scope(local_symbols); + break; + } + + // Strings get inserted into the global string list + // The STRING_LITERAL node gets replaced by a STRING_LIST_REFERENCE node + case STRING_LITERAL: + { + size_t position = add_string(node->data.string_literal); + node->type = STRING_LIST_REFERENCE; + node->data.string_list_index = position; + break; + } + + // For all other nodes, recurse through their children + default: + for (int i = 0; i < node->n_children; i++) + bind_names(local_symbols, node->children[i]); + break; + } +} + +// Prints the given symbol table, with sequence number, symbol names and types. +// When printing function symbols, its local symbol table is recursively printed, with indentation. +static void print_symbol_table(symbol_table_t* table, int nesting) +{ + for (size_t i = 0; i < table->n_symbols; i++) + { + symbol_t* symbol = table->symbols[i]; + + printf( + "%*s%ld: %s(%s)\n", + nesting * 4, + "", + symbol->sequence_number, + SYMBOL_TYPE_NAMES[symbol->type], + symbol->name); + + // If the symbol is a function, print its local symbol table as well + if (symbol->type == SYMBOL_FUNCTION) + print_symbol_table(symbol->function_symtable, nesting + 1); + } +} + +// Frees up the memory used by the global symbol table, all local symbol tables, and their symbols +static void destroy_symbol_tables(void) +{ + // First destory all local symbol tables, by looking for functions among the globals + for (int i = 0; i < global_symbols->n_symbols; i++) + { + if (global_symbols->symbols[i]->type == SYMBOL_FUNCTION) + symbol_table_destroy(global_symbols->symbols[i]->function_symtable); + } + // Then destroy the global symbol table + symbol_table_destroy(global_symbols); +} + +// Declaration of global string list +char** string_list; +size_t string_list_len; +static size_t string_list_capacity; + +// Adds the given string to the global string list, resizing if needed. +// Takes ownership of the string, and returns its position in the string list. +static size_t add_string(char* string) +{ + if (string_list_len + 1 >= string_list_capacity) + { + string_list_capacity = string_list_capacity * 2 + 8; + string_list = realloc(string_list, string_list_capacity * sizeof(char*)); + } + string_list[string_list_len] = string; + return string_list_len++; +} + +// Prints all strings added to the global string list +static void print_string_list(void) +{ + for (size_t i = 0; i < string_list_len; i++) + printf("%ld: %s\n", i, string_list[i]); +} + +// Frees all strings in the global string list, and the string list itself +static void destroy_string_list(void) +{ + for (int i = 0; i < string_list_len; i++) + free(string_list[i]); + free(string_list); +} diff --git a/ps5/src/symbols.h b/ps5/src/symbols.h new file mode 100644 index 0000000..8b7a72c --- /dev/null +++ b/ps5/src/symbols.h @@ -0,0 +1,59 @@ +#ifndef SYMBOLS_H +#define SYMBOLS_H + +#include "symbol_table.h" +#include + +typedef enum +{ + SYMBOL_GLOBAL_VAR, + SYMBOL_GLOBAL_ARRAY, + SYMBOL_FUNCTION, + SYMBOL_PARAMETER, + SYMBOL_LOCAL_VAR, +} symtype_t; + +// Use as a normal array, to get the name of a symbol type: SYMBOL_TYPE_NAMES[symbol->type] +#define SYMBOL_TYPE_NAMES \ + ((const char*[]){ \ + [SYMBOL_GLOBAL_VAR] = "GLOBAL_VAR", \ + [SYMBOL_GLOBAL_ARRAY] = "GLOBAL_ARRAY", \ + [SYMBOL_FUNCTION] = "FUNCTION", \ + [SYMBOL_PARAMETER] = "PARAMETER", \ + [SYMBOL_LOCAL_VAR] = "LOCAL_VAR"}) + +// Struct representing the definition of a symbol +typedef struct symbol +{ + char* name; // Symbol name ( not owned ) + symtype_t type; // Symbol type + node_t* node; // The AST node that defined this symbol ( not owned ) + size_t sequence_number; // Sequence number in the symbol table this symbol belongs to. + // assigned automatically upon insertion in a symbol table. + + // Global variables and arrays have function_symtable = NULL + // Functions point to their own symbol tables here, but the function itself is a global symbol + // Parameters and local variables point to the symtable they belong to + struct symbol_table* function_symtable; +} symbol_t; + +// Global symbol table, which contains and owns all global symbols. +// All function symbols in the global symbol table have pointers to their own local symbol table. +extern symbol_table_t* global_symbols; + +// Global string list, owns all contained strings +extern char** string_list; +extern size_t string_list_len; + +// Traverses the abstract syntax tree and creates symbol tables, both global and local. +// Places strings in the string_list, and turns STRING_LITERAL nodes into STRING_LIST_REFERENCEs. +void create_tables(void); + +// Outputs all global and local symbol tables, and the string list. +// Lastly outputs the abstract syntax tree with references to symbols +void print_tables(void); + +// Clean up all memory owned by symbol tables +void destroy_tables(void); + +#endif // SYMBOLS_H diff --git a/ps5/src/tree.c b/ps5/src/tree.c new file mode 100644 index 0000000..7a8bf80 --- /dev/null +++ b/ps5/src/tree.c @@ -0,0 +1,385 @@ +#include "vslc.h" + +// Global root for abstract syntax tree +node_t* root; + +// Declarations of helper functions defined further down in this file +static void node_print(node_t* node, int nesting); +static node_t* simplify_subtree(node_t* node); +static void node_finalize(node_t* discard); +static void destroy_subtree(node_t* discard); + +// Initialize a node with the given type and children +node_t* node_create(node_type_t type, size_t n_children, ...) +{ + node_t* result = malloc(sizeof(node_t)); + + // Initialize every field in the struct + *result = (node_t){ + .type = type, + .n_children = n_children, + .children = malloc(n_children * sizeof(node_t*)), + .symbol = NULL, + }; + + // Read each child node from the va_list + va_list child_list; + va_start(child_list, n_children); + for (size_t i = 0; i < n_children; i++) + { + result->children[i] = va_arg(child_list, node_t*); + } + va_end(child_list); + + return result; +} + +// Append an element to the given LIST node, returns the list node +node_t* append_to_list_node(node_t* list_node, node_t* element) +{ + assert(list_node->type == LIST); + + // Calculate the minimum size of the new allocation + size_t min_allocation_size = list_node->n_children + 1; + + // Round up to the next power of two + size_t new_allocation_size = 1; + while (new_allocation_size < min_allocation_size) + new_allocation_size *= 2; + + // Resize the allocation + list_node->children = realloc(list_node->children, new_allocation_size * sizeof(node_t*)); + + // Insert the new element and increase child count by 1 + list_node->children[list_node->n_children] = element; + list_node->n_children++; + + return list_node; +} + +// Outputs the entire syntax tree to the terminal +void print_syntax_tree(void) +{ + // If the environment variable GRAPHVIZ_OUTPUT is set, print a GraphViz graph in the dot format + if (getenv("GRAPHVIZ_OUTPUT") != NULL) + graphviz_node_print(root); + else + node_print(root, 0); +} + +// Performs constant folding and replaces nodes with simpler nodes +void simplify_syntax_tree(void) +{ + root = simplify_subtree(root); +} + +// Frees all memory held by the syntax tree +void destroy_syntax_tree(void) +{ + destroy_subtree(root); + root = NULL; +} + +// The rest of this file contains private helper functions used by the above functions + +// Prints out the given node and all its children recursively +static void node_print(node_t* node, int nesting) +{ + // Indent the line based on how deep the node is in the syntax tree + printf("%*s", nesting, ""); + + if (node == NULL) + { + printf("(NULL)\n"); + return; + } + + printf("%s", NODE_TYPE_NAMES[node->type]); + + // For nodes with extra data, include it in the printout + switch (node->type) + { + case OPERATOR: + printf(" (%s)", node->data.operator); + break; + case IDENTIFIER: + printf(" (%s)", node->data.identifier); + break; + case NUMBER_LITERAL: + printf(" (%ld)", node->data.number_literal); + break; + case STRING_LITERAL: + printf(" (%s)", node->data.string_literal); + break; + case STRING_LIST_REFERENCE: + printf(" (%zu)", node->data.string_list_index); + break; + default: + break; + } + + // If the node is a reference to a symbol, print its type and number + if (node->symbol) + { + printf(" %s(%zu)", SYMBOL_TYPE_NAMES[node->symbol->type], node->symbol->sequence_number); + } + + putchar('\n'); + + // Recursively print children, with some more indentation + for (size_t i = 0; i < node->n_children; i++) + node_print(node->children[i], nesting + 1); +} + +// If the given OPERATOR node is "and" or "or", converts it to a ternary ?: operator like so: +// a and b ===> a ? b : 0 +// a or b ===> a ? 1 : b +static node_t* convert_operator(node_t* node) +{ + assert(node->type == OPERATOR); + const char* op = node->data.operator; + + if (strcmp(op, "and") == 0) + { + node_t* lhs = node->children[0]; + node_t* rhs = node->children[1]; + node_t* zero = node_create(NUMBER_LITERAL, 0); + zero->data.number_literal = 0; + node_t* ternary = node_create(OPERATOR, 3, lhs, rhs, zero); + ternary->data.operator= "?:"; + + node_finalize(node); + return ternary; + } + + if (strcmp(op, "or") == 0) + { + node_t* lhs = node->children[0]; + node_t* rhs = node->children[1]; + node_t* one = node_create(NUMBER_LITERAL, 0); + one->data.number_literal = 1; + node_t* ternary = node_create(OPERATOR, 3, lhs, one, rhs); + ternary->data.operator= "?:"; + + node_finalize(node); + return ternary; + } + + return node; +} + +// Constant folds the given OPERATOR node, if all its children are NUMBER_LITERAL +static node_t* constant_fold_operator(node_t* node) +{ + assert(node->type == OPERATOR); + + // Check that all operands are NUMBER_LITERALs + for (size_t i = 0; i < node->n_children; i++) + if (node->children[i]->type != NUMBER_LITERAL) + return node; + + const char* op = node->data.operator; + + // This is where we store the result of the constant fold + int64_t result; + + if (node->n_children == 1) + { + int64_t operand = node->children[0]->data.number_literal; + if (strcmp(op, "-") == 0) + result = -operand; + else if (strcmp(op, "!") == 0) + result = !operand; + else + assert(false && "Unknown unary operator"); + } + else if (node->n_children == 2) + { + // We do not need to handle "and" or "or" here, as they have been converted to ternary ?: + int64_t lhs = node->children[0]->data.number_literal; + int64_t rhs = node->children[1]->data.number_literal; + if (strcmp(op, "==") == 0) + result = lhs == rhs; + else if (strcmp(op, "!=") == 0) + result = lhs != rhs; + else if (strcmp(op, "<") == 0) + result = lhs < rhs; + else if (strcmp(op, "<=") == 0) + result = lhs <= rhs; + else if (strcmp(op, ">") == 0) + result = lhs > rhs; + else if (strcmp(op, ">=") == 0) + result = lhs >= rhs; + else if (strcmp(op, "+") == 0) + result = lhs + rhs; + else if (strcmp(op, "-") == 0) + result = lhs - rhs; + else if (strcmp(op, "*") == 0) + result = lhs * rhs; + else if (strcmp(op, "/") == 0) + result = lhs / rhs; + else + assert(false && "Unknown binary operator"); + } + else if (node->n_children == 3) + { + int64_t condition = node->children[0]->data.number_literal; + int64_t lhs = node->children[1]->data.number_literal; + int64_t rhs = node->children[2]->data.number_literal; + if (strcmp(op, "?:") == 0) + result = condition ? lhs : rhs; + else + assert(false && "Unknown ternary operator"); + } + + // Free all children, turn the node into a NUMBER_LITERAL + for (size_t i = 0; i < node->n_children; i++) + destroy_subtree(node->children[i]); + + node->type = NUMBER_LITERAL; + node->data.number_literal = result; + node->n_children = 0; + return node; +} + +// Convert PRINTLN_STATEMENT into PRINT_STATEMENT by appending an extra "\n" +static node_t* simplify_println_statement(node_t* node) +{ + assert(node->type == PRINTLN_STATEMENT); + node->type = PRINT_STATEMENT; + + // Create a string literal containing "\n" + node_t* newline_literal = node_create(STRING_LITERAL, 0); + newline_literal->data.string_literal = strdup("\"\\n\""); + append_to_list_node(node->children[0], newline_literal); + + return node; +} + +// Flatten all LOCAL_VARIABLE_DELCARATION nodes in the block, by converting them +// into individual LOCAL_VARIABLE nodes, and splitting all variable initializations +// into separate ASSIGNMENT_STATEMENT nodes. +static node_t* flatten_variable_declarations(node_t* block) +{ + assert(block->type == BLOCK); + + node_t* old_list = block->children[0]; + node_t* new_list = node_create(LIST, 0); + block->children[0] = new_list; + + // Move over statements from the old list to the new list + // LOCAL_VARIABLE_DECLARATIONs are split up + for (size_t i = 0; i < old_list->n_children; i++) + { + node_t* node = old_list->children[i]; + if (node->type != LOCAL_VARIABLE_DECLARATION) + { + append_to_list_node(new_list, node); + continue; + } + + // Copy over each LOCAL_VARIABLE node to the new list + node_t* list = node->children[0]; + for (size_t j = 0; j < list->n_children; j++) + { + node_t* local_variable = list->children[j]; + append_to_list_node(new_list, local_variable); + + // if the local variable has an initial value, move it to an assignment statement + if (local_variable->n_children == 2) + { + // Only keep the identifier child + local_variable->n_children = 1; + + node_t* identifier = local_variable->children[0]; + node_t* expression = local_variable->children[1]; + + // Create the assignment statement using the expression child + node_t* dup_identifier = node_create(IDENTIFIER, 0); + dup_identifier->data.identifier = strdup(identifier->data.identifier); + node_t* assignment_statement = + node_create(ASSIGNMENT_STATEMENT, 2, dup_identifier, expression); + append_to_list_node(new_list, assignment_statement); + } + } + // Delete the LOCAL_VARIABLE_DECLARATION node and its LIST + list->n_children = 0; + destroy_subtree(node); + } + + // Clean up the node used for the old list, but do not free any children + node_finalize(old_list); + + return block; +} + +// Recursively performs simplifcation of the syntax tree: +// - replacing "and" and "or" operators with the ternary ?: operator +// - constant folding operators where all operands are NUMBER_DATA +// - replacing all PRINTLN_STATEMENT with PRINT_STATEMENT with an extra "\n" +// - flattening LOCAL_VARIABLE_DECLARATION nodes into LOCAL_VARIABLE and ASSIGNMENT +// +// Returns the root of the new subtree. +// Any node that is detached from the tree by this operation must be freed, to avoid memory leaks. +static node_t* simplify_subtree(node_t* node) +{ + if (node == NULL) + return node; + + // First simplify all child nodes + for (size_t i = 0; i < node->n_children; i++) + node->children[i] = simplify_subtree(node->children[i]); + + if (node->type == OPERATOR) + { + node = convert_operator(node); + return constant_fold_operator(node); + } + + if (node->type == PRINTLN_STATEMENT) + return simplify_println_statement(node); + + if (node->type == BLOCK) + return flatten_variable_declarations(node); + + return node; +} + +// Frees the memory owned by the given node, but does not touch its children +static void node_finalize(node_t* discard) +{ + if (discard == NULL) + return; + + // Only free data if the data field is owned by the node + switch (discard->type) + { + case IDENTIFIER: + free(discard->data.identifier); + break; + case STRING_LITERAL: + free(discard->data.string_literal); + break; + default: + break; + } + free(discard->children); + free(discard); +} + +// Recursively frees the memory owned by the given node, and all its children +static void destroy_subtree(node_t* discard) +{ + if (discard == NULL) + return; + + for (size_t i = 0; i < discard->n_children; i++) + destroy_subtree(discard->children[i]); + node_finalize(discard); +} + +// Definition of the global string array NODE_TYPE_NAMES +const char* NODE_TYPE_NAMES[NODE_TYPE_COUNT] = { +#define NODE_TYPE(node_type) #node_type +#include "nodetypes.h" +}; diff --git a/ps5/src/tree.h b/ps5/src/tree.h new file mode 100644 index 0000000..a73caa1 --- /dev/null +++ b/ps5/src/tree.h @@ -0,0 +1,64 @@ +#ifndef TREE_H +#define TREE_H + +#include +#include + +// Create the node_type_t enum containing all node types defined in nodetypes.h +typedef enum +{ + +#define NODE_TYPE(node_type) node_type +#include "nodetypes.h" + NODE_TYPE_COUNT +} node_type_t; + +// Array containing human-readable names for all node types +extern const char* NODE_TYPE_NAMES[NODE_TYPE_COUNT]; + +// This is the tree node structure for the abstract syntax tree +typedef struct node +{ + node_type_t type; + struct node** children; // An owned list of pointers to child nodes + size_t n_children; // The length of the list of child nodes + + // At most one of the data fields can be used at once. + // The node's type decides which field is active, if any + union + { + const char* operator; // pointer to constant string, such as "+". Not owned + char* identifier; // owned heap allocation. The identifier as a string + int64_t number_literal; // the literal integer value + char* string_literal; // owned heap allocation. Includes the surrounding "quotation marks" + size_t string_list_index; // position in global string list + } data; + + // A pointer to the symbol this node references. Not owned. + // Only used by IDENTIFIER nodes that reference symbols defined elsewhere. + struct symbol* symbol; +} node_t; + +// Global root for parse tree and abstract syntax tree +extern node_t* root; + +// The node creation function, used by the parser +node_t* node_create(node_type_t type, size_t n_children, ...); + +// Append an element to the given LIST node, returns the list node +node_t* append_to_list_node(node_t* list_node, node_t* element); + +// Outputs the entire syntax tree to the terminal +void print_syntax_tree(void); + +// Performs constant folding and node replacements +void simplify_syntax_tree(void); + +// Cleans up the entire syntax tree +void destroy_syntax_tree(void); + +// Special function used when syntax trees are output as graphviz graphs. +// Implemented in graphviz_output.c +void graphviz_node_print(node_t* root); + +#endif // TREE_H diff --git a/ps5/src/vslc.c b/ps5/src/vslc.c new file mode 100644 index 0000000..9b97a6d --- /dev/null +++ b/ps5/src/vslc.c @@ -0,0 +1,84 @@ +#include "vslc.h" + +#include + +static bool print_full_tree = false; +static bool print_simplified_tree = false; +static bool print_symbol_table_contents = false; +static bool print_generated_assembly = false; + +static const char* usage = "Compiler for VSL. The input program is read from stdin." + "\n" + "Options:\n" + "\t -h \t Output this text and exit\n" + "\t -t \t Output the abstract syntax tree\n" + "\t -T \t Output the abstract syntax tree after constant folding\n" + "\t -s \t Output the symbol table contents\n" + "\t -c \t Compile and print assembly output\n"; + +// Command line option parsing +static void options(int argc, char** argv) +{ + if (argc == 1) + { + fprintf(stderr, "%s: expected at last one option. See -h for help\n", argv[0]); + exit(EXIT_FAILURE); + } + + while (true) + { + switch (getopt(argc, argv, "htTsc")) + { + default: // Unrecognized option + fprintf(stderr, "%s: See -h for help\n", argv[0]); + exit(EXIT_FAILURE); + case 'h': + printf("%s:\n%s", argv[0], usage); + exit(EXIT_SUCCESS); + case 't': + print_full_tree = true; + break; + case 'T': + print_simplified_tree = true; + break; + case 's': + print_symbol_table_contents = true; + break; + case 'c': + print_generated_assembly = true; + break; + case -1: + return; // Done parsing options + } + } +} + +// Entry point +int main(int argc, char** argv) +{ + options(argc, argv); + + yyparse(); // Generated from grammar/bison, constructs syntax tree + yylex_destroy(); // Free buffers used by flex + + // Operations in tree.c + if (print_full_tree) + print_syntax_tree(); + + simplify_syntax_tree(); + + if (print_simplified_tree) + print_syntax_tree(); + + // Operations in symbols.c + create_tables(); + if (print_symbol_table_contents) + print_tables(); + + // Operations in generator.c + if (print_generated_assembly) + generate_program(); + + destroy_tables(); // In symbols.c + destroy_syntax_tree(); // In tree.c +} diff --git a/ps5/src/vslc.h b/ps5/src/vslc.h new file mode 100644 index 0000000..d250a05 --- /dev/null +++ b/ps5/src/vslc.h @@ -0,0 +1,26 @@ +#ifndef VSLC_H +#define VSLC_H +#include +#include +#include +#include +#include +#include +#include + +// Definition of the tree node type, and functions for handling the parse tree +#include "tree.h" + +// Definition of the symbol table, and functions for building it +#include "symbols.h" + +// Function for generating machine code, in generator.c +void generate_program(void); + +// The main driver function of the parser generated by bison +int yyparse(); + +// A "hidden" cleanup function in flex +int yylex_destroy(); + +#endif // VSLC_H diff --git a/ps5/vsl_programs/codegen-tester.py b/ps5/vsl_programs/codegen-tester.py new file mode 100755 index 0000000..4bc0b44 --- /dev/null +++ b/ps5/vsl_programs/codegen-tester.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +import sys +import subprocess +import os.path + +name, *args = sys.argv + +USAGE = f""" +Usage: {name} + +For each occurance of a VSL comment block starting with +//TESTCASE: +The corresponding compiler executable file.out is executed with the given . +Output is compared against the rest of the comment block. +If they are different, the difference is printed and the test fails. +""".strip() + +TESTCASE_LINE = "//TESTCASE:" + +def error(text, message=None): + print(f"{name}: error: {text}") + if message is not None: + print(message) + sys.exit(1) + +if len(args) != 1: + error("expected one input .vsl file", message=USAGE) + +vsl_file, = args + +if not os.path.isfile(vsl_file): + error(f"file not found: {vsl_file}") + +out_file = vsl_file[:vsl_file.rindex(".")] + ".out" + +if not os.path.isfile(out_file): + error(f"file not found: {out_file}") + +with open(vsl_file, "r", encoding="utf-8") as vsl_fd: + lines = vsl_fd.read().splitlines() + +tests = [] + +i = 0 +while i < len(lines): + line = lines[i] + if line.startswith(TESTCASE_LINE): + args = line[len(TESTCASE_LINE):].split(" ") + args = [arg for arg in args if len(arg)] + + expected_output = [] + i += 1 + while i < len(lines) and lines[i].startswith("//") and not lines[i].startswith(TESTCASE_LINE): + expected_output.append(lines[i][2:]) + i += 1 + + tests.append((args, expected_output)) + else: + i += 1 + +print(f"Running {len(tests)} test cases for file {vsl_file}") + +# Ensure that relative paths to executables start with "./" +out_file = os.path.join(".", out_file) + +for args, expected_output in tests: + print(f" Running {out_file} {' '.join(args)}") + proc = subprocess.run([out_file] + args, capture_output=True, text=True, check=False, timeout=5) + result_lines = proc.stdout.strip().split('\n') + + if len(result_lines) != len(expected_output) or any(a != b for a, b in zip(result_lines, expected_output)): + message = ["EXPECTED --------", + "\n".join(expected_output), + "ACTUAL ----------", + "\n".join(result_lines), + "-----------------"] + error("actual output didn't match expected output", message="\n".join(message)) diff --git a/ps5/vsl_programs/ps2-parser/arrays.vsl b/ps5/vsl_programs/ps2-parser/arrays.vsl new file mode 100644 index 0000000..52ca0f9 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/arrays.vsl @@ -0,0 +1,14 @@ + +var array[3], other[20] + +func sum() { + return array[0] + array[1] + array[2] +} + +func main() { + array[0] = 5 + array[1] = 1 + array[2] = array[1] + other[sum()] = sum() + println("Should be 7:", other[7]) +} diff --git a/ps5/vsl_programs/ps2-parser/expected/arrays.ast b/ps5/vsl_programs/ps2-parser/expected/arrays.ast new file mode 100644 index 0000000..9254fb8 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/expected/arrays.ast @@ -0,0 +1,63 @@ +LIST + GLOBAL_VARIABLE_DECLARATION + LIST + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (3) + ARRAY_INDEXING + IDENTIFIER (other) + NUMBER_LITERAL (20) + FUNCTION + IDENTIFIER (sum) + LIST + BLOCK + LIST + RETURN_STATEMENT + OPERATOR (+) + OPERATOR (+) + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (0) + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (1) + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (2) + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + ASSIGNMENT_STATEMENT + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (0) + NUMBER_LITERAL (5) + ASSIGNMENT_STATEMENT + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (1) + NUMBER_LITERAL (1) + ASSIGNMENT_STATEMENT + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (2) + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (1) + ASSIGNMENT_STATEMENT + ARRAY_INDEXING + IDENTIFIER (other) + FUNCTION_CALL + IDENTIFIER (sum) + LIST + FUNCTION_CALL + IDENTIFIER (sum) + LIST + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Should be 7:") + ARRAY_INDEXING + IDENTIFIER (other) + NUMBER_LITERAL (7) diff --git a/ps5/vsl_programs/ps2-parser/expected/if.ast b/ps5/vsl_programs/ps2-parser/expected/if.ast new file mode 100644 index 0000000..2bc87cb --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/expected/if.ast @@ -0,0 +1,60 @@ +LIST + FUNCTION + IDENTIFIER (main) + LIST + IDENTIFIER (a) + IDENTIFIER (b) + BLOCK + LIST + IF_STATEMENT + NUMBER_LITERAL (1) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Always") + IF_STATEMENT + NUMBER_LITERAL (0) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Never") + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("This, however!") + IF_STATEMENT + OPERATOR (>) + IDENTIFIER (a) + IDENTIFIER (b) + BLOCK + LIST + PRINTLN_STATEMENT + LIST + IDENTIFIER (a) + STRING_LITERAL (">") + IDENTIFIER (b) + IF_STATEMENT + OPERATOR (<) + IDENTIFIER (a) + IDENTIFIER (b) + BLOCK + LIST + PRINTLN_STATEMENT + LIST + IDENTIFIER (a) + STRING_LITERAL ("<") + IDENTIFIER (b) + BLOCK + LIST + PRINTLN_STATEMENT + LIST + IDENTIFIER (a) + STRING_LITERAL ("=") + IDENTIFIER (b) + IF_STATEMENT + IDENTIFIER (a) + IF_STATEMENT + IDENTIFIER (b) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("a & b") + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("a, but not b") diff --git a/ps5/vsl_programs/ps2-parser/expected/operators.ast b/ps5/vsl_programs/ps2-parser/expected/operators.ast new file mode 100644 index 0000000..3e1c5fc --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/expected/operators.ast @@ -0,0 +1,146 @@ +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (a) + NUMBER_LITERAL (1) + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (b) + OPERATOR (-) + OPERATOR (-) + NUMBER_LITERAL (4) + NUMBER_LITERAL (1) + NUMBER_LITERAL (1) + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (c) + OPERATOR (+) + OPERATOR (+) + OPERATOR (*) + OPERATOR (-) + IDENTIFIER (a) + IDENTIFIER (b) + OPERATOR (*) + OPERATOR (*) + NUMBER_LITERAL (4) + IDENTIFIER (a) + NUMBER_LITERAL (1) + NUMBER_LITERAL (2) + ASSIGNMENT_STATEMENT + IDENTIFIER (a) + OPERATOR (+) + OPERATOR (/) + IDENTIFIER (a) + NUMBER_LITERAL (2) + NUMBER_LITERAL (1) + IF_STATEMENT + OPERATOR (<) + OPERATOR (*) + IDENTIFIER (a) + IDENTIFIER (a) + OPERATOR (+) + IDENTIFIER (b) + IDENTIFIER (c) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Yes") + IF_STATEMENT + OPERATOR (or) + OPERATOR (==) + IDENTIFIER (a) + NUMBER_LITERAL (1) + OPERATOR (==) + IDENTIFIER (b) + NUMBER_LITERAL (2) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Either!") + IF_STATEMENT + OPERATOR (and) + OPERATOR (==) + IDENTIFIER (a) + NUMBER_LITERAL (1) + OPERATOR (==) + IDENTIFIER (b) + NUMBER_LITERAL (2) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Both!") + IF_STATEMENT + OPERATOR (==) + OPERATOR (!) + IDENTIFIER (a) + NUMBER_LITERAL (0) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Yup") + IF_STATEMENT + OPERATOR (==) + OPERATOR (<=) + IDENTIFIER (a) + IDENTIFIER (b) + OPERATOR (>) + IDENTIFIER (b) + IDENTIFIER (a) + PRINT_STATEMENT + LIST + STRING_LITERAL ("Alawys!") + IF_STATEMENT + OPERATOR (!=) + OPERATOR (<=) + IDENTIFIER (a) + IDENTIFIER (b) + OPERATOR (>=) + IDENTIFIER (a) + IDENTIFIER (b) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("a != b") + IF_STATEMENT + OPERATOR (==) + OPERATOR (==) + IDENTIFIER (a) + IDENTIFIER (b) + OPERATOR (!=) + IDENTIFIER (a) + IDENTIFIER (b) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Never!") + ASSIGNMENT_STATEMENT + IDENTIFIER (a) + OPERATOR (?:) + OPERATOR (or) + OPERATOR (>) + IDENTIFIER (a) + NUMBER_LITERAL (4) + OPERATOR (<) + IDENTIFIER (b) + NUMBER_LITERAL (2) + OPERATOR (+) + IDENTIFIER (a) + NUMBER_LITERAL (4) + OPERATOR (-) + IDENTIFIER (b) + NUMBER_LITERAL (2) + ASSIGNMENT_STATEMENT + IDENTIFIER (c) + OPERATOR (+) + OPERATOR (<=) + IDENTIFIER (a) + IDENTIFIER (b) + OPERATOR (-) + OPERATOR (-) + OPERATOR (>=) + IDENTIFIER (a) + IDENTIFIER (b) + RETURN_STATEMENT + IDENTIFIER (c) diff --git a/ps5/vsl_programs/ps2-parser/expected/simple-assignment.ast b/ps5/vsl_programs/ps2-parser/expected/simple-assignment.ast new file mode 100644 index 0000000..08a95f8 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/expected/simple-assignment.ast @@ -0,0 +1,25 @@ +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (localVariable) + ASSIGNMENT_STATEMENT + IDENTIFIER (globalVariable) + NUMBER_LITERAL (10) + ASSIGNMENT_STATEMENT + IDENTIFIER (localVariable) + NUMBER_LITERAL (3) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("Global:") + IDENTIFIER (globalVariable) + STRING_LITERAL (" local:") + IDENTIFIER (localVariable) + GLOBAL_VARIABLE_DECLARATION + LIST + IDENTIFIER (globalVariable) diff --git a/ps5/vsl_programs/ps2-parser/expected/simple-functions.ast b/ps5/vsl_programs/ps2-parser/expected/simple-functions.ast new file mode 100644 index 0000000..1d2d35c --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/expected/simple-functions.ast @@ -0,0 +1,51 @@ +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + PRINT_STATEMENT + LIST + STRING_LITERAL ("Should be 7:") + FUNCTION_CALL + IDENTIFIER (identity) + LIST + FUNCTION_CALL + IDENTIFIER (first) + LIST + FUNCTION_CALL + IDENTIFIER (second) + LIST + NUMBER_LITERAL (5) + NUMBER_LITERAL (7) + FUNCTION_CALL + IDENTIFIER (first) + LIST + NUMBER_LITERAL (2) + NUMBER_LITERAL (9) + FUNCTION + IDENTIFIER (identity) + LIST + IDENTIFIER (argument) + BLOCK + LIST + RETURN_STATEMENT + IDENTIFIER (argument) + FUNCTION + IDENTIFIER (first) + LIST + IDENTIFIER (a) + IDENTIFIER (b) + BLOCK + LIST + RETURN_STATEMENT + IDENTIFIER (a) + FUNCTION + IDENTIFIER (second) + LIST + IDENTIFIER (a) + IDENTIFIER (b) + BLOCK + LIST + RETURN_STATEMENT + IDENTIFIER (b) diff --git a/ps5/vsl_programs/ps2-parser/expected/simple-hello.ast b/ps5/vsl_programs/ps2-parser/expected/simple-hello.ast new file mode 100644 index 0000000..43ac532 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/expected/simple-hello.ast @@ -0,0 +1,12 @@ +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + PRINT_STATEMENT + LIST + STRING_LITERAL ("Hello ") + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("World!") diff --git a/ps5/vsl_programs/ps2-parser/expected/variables.ast b/ps5/vsl_programs/ps2-parser/expected/variables.ast new file mode 100644 index 0000000..c5b6252 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/expected/variables.ast @@ -0,0 +1,88 @@ +LIST + GLOBAL_VARIABLE_DECLARATION + LIST + IDENTIFIER (global) + ARRAY_INDEXING + IDENTIFIER (myArray) + NUMBER_LITERAL (10) + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (a) + LOCAL_VARIABLE + IDENTIFIER (b) + NUMBER_LITERAL (2) + ASSIGNMENT_STATEMENT + IDENTIFIER (a) + NUMBER_LITERAL (5) + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (c) + ASSIGNMENT_STATEMENT + IDENTIFIER (global) + NUMBER_LITERAL (3) + BLOCK + LIST + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (d) + OPERATOR (+) + IDENTIFIER (a) + OPERATOR (*) + IDENTIFIER (b) + IDENTIFIER (c) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("d:") + IDENTIFIER (d) + IF_STATEMENT + NUMBER_LITERAL (1) + BLOCK + LIST + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (x) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + OPERATOR (*) + OPERATOR (*) + IDENTIFIER (a) + IDENTIFIER (b) + IDENTIFIER (c) + IF_STATEMENT + OPERATOR (>) + IDENTIFIER (x) + NUMBER_LITERAL (0) + BLOCK + LIST + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (x) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + OPERATOR (+) + IDENTIFIER (a) + IDENTIFIER (global) + BLOCK + LIST + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + IDENTIFIER (a) + ASSIGNMENT_STATEMENT + ARRAY_INDEXING + IDENTIFIER (myArray) + IDENTIFIER (global) + NUMBER_LITERAL (2) + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("x:") + IDENTIFIER (x) diff --git a/ps5/vsl_programs/ps2-parser/expected/while.ast b/ps5/vsl_programs/ps2-parser/expected/while.ast new file mode 100644 index 0000000..382b76c --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/expected/while.ast @@ -0,0 +1,56 @@ +LIST + FUNCTION + IDENTIFIER (callMe) + LIST + IDENTIFIER (i) + BLOCK + LIST + PRINTLN_STATEMENT + LIST + STRING_LITERAL ("i is now") + IDENTIFIER (i) + FUNCTION + IDENTIFIER (main) + LIST + IDENTIFIER (start) + IDENTIFIER (end) + BLOCK + LIST + LOCAL_VARIABLE_DECLARATION + LIST + LOCAL_VARIABLE + IDENTIFIER (counter) + IDENTIFIER (start) + WHILE_STATEMENT + OPERATOR (<) + IDENTIFIER (counter) + IDENTIFIER (end) + BLOCK + LIST + FUNCTION_CALL + IDENTIFIER (callMe) + LIST + IDENTIFIER (counter) + ASSIGNMENT_STATEMENT + IDENTIFIER (counter) + OPERATOR (+) + IDENTIFIER (counter) + NUMBER_LITERAL (1) + WHILE_STATEMENT + NUMBER_LITERAL (1) + BLOCK + LIST + ASSIGNMENT_STATEMENT + IDENTIFIER (counter) + OPERATOR (-) + IDENTIFIER (counter) + NUMBER_LITERAL (1) + IF_STATEMENT + OPERATOR (<) + IDENTIFIER (counter) + IDENTIFIER (start) + BREAK_STATEMENT + FUNCTION_CALL + IDENTIFIER (callMe) + LIST + IDENTIFIER (counter) diff --git a/ps5/vsl_programs/ps2-parser/if.vsl b/ps5/vsl_programs/ps2-parser/if.vsl new file mode 100644 index 0000000..32b27a4 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/if.vsl @@ -0,0 +1,27 @@ + +func main(a, b) { + if (1) + println("Always") + + if (0) + println("Never") + else + println("This, however!") + + if (a > b) { + println(a, ">", b) + } + else if (a < b) { + println(a, "<", b) + } + else { + println(a, "=", b) + } + + // Now test dangling else + if (a) + if (b) + println("a & b") + else + println("a, but not b") +} diff --git a/ps5/vsl_programs/ps2-parser/operators.vsl b/ps5/vsl_programs/ps2-parser/operators.vsl new file mode 100644 index 0000000..89b3b24 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/operators.vsl @@ -0,0 +1,34 @@ + +func main() { + var a = 1 + var b = 4 - 1 - 1 + var c = -a * b + 4 * a * 1 + 2 + + a = a / 2 + 1 + + if (a * a < b + c) + println("Yes") + + if (a == 1 or b == 2) + println("Either!") + + if (a == 1 and b == 2) + println("Both!") + + if (!a == 0) + println("Yup") + + if (a <= b == b > a) + print("Alawys!") + + if (a <= b != a >= b) + println("a != b") + + if (a == b == (a != b)) + println("Never!") + + a = a > 4 or b < 2 ? a + 4 : b - 2 + + c = (a <= b) + --(a >= b) + return c +} diff --git a/ps5/vsl_programs/ps2-parser/simple-assignment.vsl b/ps5/vsl_programs/ps2-parser/simple-assignment.vsl new file mode 100644 index 0000000..84ae100 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/simple-assignment.vsl @@ -0,0 +1,12 @@ + +func main() { + var localVariable + + globalVariable = 10 + localVariable = 3 + + println("Global:", globalVariable, " local:", localVariable) +} + +// Global variables can be declared anywhere in the file +var globalVariable diff --git a/ps5/vsl_programs/ps2-parser/simple-functions.vsl b/ps5/vsl_programs/ps2-parser/simple-functions.vsl new file mode 100644 index 0000000..b5864c9 --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/simple-functions.vsl @@ -0,0 +1,16 @@ + +func main() { + print("Should be 7:", identity(first(second(5, 7), first(2, 9)))) +} + +func identity(argument) { + return argument +} + +func first(a, b) { + return a +} + +func second(a, b) { + return b +} diff --git a/ps5/vsl_programs/ps2-parser/simple-hello.vsl b/ps5/vsl_programs/ps2-parser/simple-hello.vsl new file mode 100644 index 0000000..387fc2c --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/simple-hello.vsl @@ -0,0 +1,5 @@ + +func main() { + print("Hello ") + println("World!") +} diff --git a/ps5/vsl_programs/ps2-parser/variables.vsl b/ps5/vsl_programs/ps2-parser/variables.vsl new file mode 100644 index 0000000..01a2ead --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/variables.vsl @@ -0,0 +1,30 @@ + +var global, myArray[10] + +func main() { + var a, b = 2 + a = 5 + var c + global = 3 + + // A block is itself a statement + { + var d = a + b * c + println("d:", d) + } + + if (1) { + var x + x = a*b*c + + if (x > 0) { + // Declare a new x, shadowing the outer x + var x + x = a + global + } else { + x = a + myArray[global] = 2 + } + println("x:", x) + } +} diff --git a/ps5/vsl_programs/ps2-parser/while.vsl b/ps5/vsl_programs/ps2-parser/while.vsl new file mode 100644 index 0000000..50a7e9d --- /dev/null +++ b/ps5/vsl_programs/ps2-parser/while.vsl @@ -0,0 +1,20 @@ + +func callMe(i) { + println("i is now", i) +} + +func main(start, end) { + var counter = start + while (counter < end) { + callMe(counter) + counter = counter + 1 + } + + // Go down again using while 1 + break + while (1) { + counter = counter - 1 + if (counter < start) + break + callMe(counter) + } +} diff --git a/ps5/vsl_programs/ps3-simplify/and-or-convert.vsl b/ps5/vsl_programs/ps3-simplify/and-or-convert.vsl new file mode 100644 index 0000000..f164e9d --- /dev/null +++ b/ps5/vsl_programs/ps3-simplify/and-or-convert.vsl @@ -0,0 +1,14 @@ +func other() { + return 1 +} + +func main(a) { + if (a and other()) + print("Hei") + + if (a or other()) + print("Yo") + + if (a > 2 or other() - 1 and a + 1 < 10) + print("Nope") +} diff --git a/ps5/vsl_programs/ps3-simplify/constant-fold.vsl b/ps5/vsl_programs/ps3-simplify/constant-fold.vsl new file mode 100644 index 0000000..5921f05 --- /dev/null +++ b/ps5/vsl_programs/ps3-simplify/constant-fold.vsl @@ -0,0 +1,26 @@ +func main() { + print(3 + 5) + print(3 - -6) + print(20 * 4) + print(111 / 11) + + print(50 and 10) + print(0 or 20) + print(0 and 1) + print(1 ? 5 : 7) + print(0 ? 20 : 30) + + return other() +} + +func other() { + var x + x = !5 == !6 + x = !(5 != 6) + x = 10 < 10 + x = 10 <= 10 + x = 10 > 10 + x = 10 >= 10 + x = 40 + 20 + x * 2 + return x +} diff --git a/ps5/vsl_programs/ps3-simplify/expected/and-or-convert.ast b/ps5/vsl_programs/ps3-simplify/expected/and-or-convert.ast new file mode 100644 index 0000000..805cc2d --- /dev/null +++ b/ps5/vsl_programs/ps3-simplify/expected/and-or-convert.ast @@ -0,0 +1,55 @@ +LIST + FUNCTION + IDENTIFIER (other) + LIST + BLOCK + LIST + RETURN_STATEMENT + NUMBER_LITERAL (1) + FUNCTION + IDENTIFIER (main) + LIST + IDENTIFIER (a) + BLOCK + LIST + IF_STATEMENT + OPERATOR (?:) + IDENTIFIER (a) + FUNCTION_CALL + IDENTIFIER (other) + LIST + NUMBER_LITERAL (0) + PRINT_STATEMENT + LIST + STRING_LITERAL ("Hei") + IF_STATEMENT + OPERATOR (?:) + IDENTIFIER (a) + NUMBER_LITERAL (1) + FUNCTION_CALL + IDENTIFIER (other) + LIST + PRINT_STATEMENT + LIST + STRING_LITERAL ("Yo") + IF_STATEMENT + OPERATOR (?:) + OPERATOR (>) + IDENTIFIER (a) + NUMBER_LITERAL (2) + NUMBER_LITERAL (1) + OPERATOR (?:) + OPERATOR (-) + FUNCTION_CALL + IDENTIFIER (other) + LIST + NUMBER_LITERAL (1) + OPERATOR (<) + OPERATOR (+) + IDENTIFIER (a) + NUMBER_LITERAL (1) + NUMBER_LITERAL (10) + NUMBER_LITERAL (0) + PRINT_STATEMENT + LIST + STRING_LITERAL ("Nope") diff --git a/ps5/vsl_programs/ps3-simplify/expected/constant-fold.ast b/ps5/vsl_programs/ps3-simplify/expected/constant-fold.ast new file mode 100644 index 0000000..3f234f0 --- /dev/null +++ b/ps5/vsl_programs/ps3-simplify/expected/constant-fold.ast @@ -0,0 +1,71 @@ +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + PRINT_STATEMENT + LIST + NUMBER_LITERAL (8) + PRINT_STATEMENT + LIST + NUMBER_LITERAL (9) + PRINT_STATEMENT + LIST + NUMBER_LITERAL (80) + PRINT_STATEMENT + LIST + NUMBER_LITERAL (10) + PRINT_STATEMENT + LIST + NUMBER_LITERAL (10) + PRINT_STATEMENT + LIST + NUMBER_LITERAL (20) + PRINT_STATEMENT + LIST + NUMBER_LITERAL (0) + PRINT_STATEMENT + LIST + NUMBER_LITERAL (5) + PRINT_STATEMENT + LIST + NUMBER_LITERAL (30) + RETURN_STATEMENT + FUNCTION_CALL + IDENTIFIER (other) + LIST + FUNCTION + IDENTIFIER (other) + LIST + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (x) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + NUMBER_LITERAL (1) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + NUMBER_LITERAL (0) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + NUMBER_LITERAL (0) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + NUMBER_LITERAL (1) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + NUMBER_LITERAL (0) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + NUMBER_LITERAL (1) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) + OPERATOR (+) + NUMBER_LITERAL (60) + OPERATOR (*) + IDENTIFIER (x) + NUMBER_LITERAL (2) + RETURN_STATEMENT + IDENTIFIER (x) diff --git a/ps5/vsl_programs/ps3-simplify/expected/println-convert.ast b/ps5/vsl_programs/ps3-simplify/expected/println-convert.ast new file mode 100644 index 0000000..9063c1d --- /dev/null +++ b/ps5/vsl_programs/ps3-simplify/expected/println-convert.ast @@ -0,0 +1,21 @@ +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + PRINT_STATEMENT + LIST + NUMBER_LITERAL (20) + STRING_LITERAL (" and ") + NUMBER_LITERAL (40) + PRINT_STATEMENT + LIST + STRING_LITERAL ("!") + STRING_LITERAL ("\n") + PRINT_STATEMENT + LIST + NUMBER_LITERAL (50) + STRING_LITERAL (" and ") + NUMBER_LITERAL (50) + STRING_LITERAL ("\n") diff --git a/ps5/vsl_programs/ps3-simplify/expected/var-convert.ast b/ps5/vsl_programs/ps3-simplify/expected/var-convert.ast new file mode 100644 index 0000000..fe7cd95 --- /dev/null +++ b/ps5/vsl_programs/ps3-simplify/expected/var-convert.ast @@ -0,0 +1,29 @@ +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (a) + LOCAL_VARIABLE + IDENTIFIER (b) + ASSIGNMENT_STATEMENT + IDENTIFIER (b) + NUMBER_LITERAL (2) + LOCAL_VARIABLE + IDENTIFIER (c) + ASSIGNMENT_STATEMENT + IDENTIFIER (c) + IDENTIFIER (b) + PRINT_STATEMENT + LIST + IDENTIFIER (b) + LOCAL_VARIABLE + IDENTIFIER (c) + ASSIGNMENT_STATEMENT + IDENTIFIER (c) + NUMBER_LITERAL (10) + PRINT_STATEMENT + LIST + IDENTIFIER (c) diff --git a/ps5/vsl_programs/ps3-simplify/println-convert.vsl b/ps5/vsl_programs/ps3-simplify/println-convert.vsl new file mode 100644 index 0000000..bd65e9d --- /dev/null +++ b/ps5/vsl_programs/ps3-simplify/println-convert.vsl @@ -0,0 +1,6 @@ +func main() { + print(20, " and ", 40) + println("!") + + println(50, " and ", 50) +} diff --git a/ps5/vsl_programs/ps3-simplify/var-convert.vsl b/ps5/vsl_programs/ps3-simplify/var-convert.vsl new file mode 100644 index 0000000..7d29d26 --- /dev/null +++ b/ps5/vsl_programs/ps3-simplify/var-convert.vsl @@ -0,0 +1,10 @@ +func main() { + var a, b = 2, c = b + + print(b) + + var c + c = 10 + + print(c) +} diff --git a/ps5/vsl_programs/ps4-symbols/expected/locals.symbols b/ps5/vsl_programs/ps4-symbols/expected/locals.symbols new file mode 100644 index 0000000..486d701 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/expected/locals.symbols @@ -0,0 +1,102 @@ +0: FUNCTION(main) + 0: LOCAL_VAR(a) + 1: LOCAL_VAR(b) + 2: LOCAL_VAR(tmp) + 3: LOCAL_VAR(tmp) +1: FUNCTION(recursive) + 0: PARAMETER(x) + 1: LOCAL_VAR(prev) + + == STRING LIST == + + == BOUND SYNTAX TREE == +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (a) + ASSIGNMENT_STATEMENT + IDENTIFIER (a) LOCAL_VAR(0) + NUMBER_LITERAL (2) + LOCAL_VARIABLE + IDENTIFIER (b) + ASSIGNMENT_STATEMENT + IDENTIFIER (b) LOCAL_VAR(1) + NUMBER_LITERAL (6) + WHILE_STATEMENT + OPERATOR (<) + OPERATOR (+) + IDENTIFIER (a) LOCAL_VAR(0) + IDENTIFIER (b) LOCAL_VAR(1) + NUMBER_LITERAL (10) + BLOCK + LIST + ASSIGNMENT_STATEMENT + IDENTIFIER (a) LOCAL_VAR(0) + OPERATOR (+) + IDENTIFIER (a) LOCAL_VAR(0) + NUMBER_LITERAL (1) + LOCAL_VARIABLE + IDENTIFIER (tmp) + ASSIGNMENT_STATEMENT + IDENTIFIER (tmp) LOCAL_VAR(2) + IDENTIFIER (a) LOCAL_VAR(0) + ASSIGNMENT_STATEMENT + IDENTIFIER (a) LOCAL_VAR(0) + IDENTIFIER (b) LOCAL_VAR(1) + ASSIGNMENT_STATEMENT + IDENTIFIER (b) LOCAL_VAR(1) + IDENTIFIER (tmp) LOCAL_VAR(2) + IF_STATEMENT + OPERATOR (<) + IDENTIFIER (a) LOCAL_VAR(0) + IDENTIFIER (b) LOCAL_VAR(1) + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (tmp) + ASSIGNMENT_STATEMENT + IDENTIFIER (tmp) LOCAL_VAR(3) + IDENTIFIER (a) LOCAL_VAR(0) + ASSIGNMENT_STATEMENT + IDENTIFIER (a) LOCAL_VAR(0) + IDENTIFIER (b) LOCAL_VAR(1) + ASSIGNMENT_STATEMENT + IDENTIFIER (b) LOCAL_VAR(1) + IDENTIFIER (tmp) LOCAL_VAR(3) + RETURN_STATEMENT + FUNCTION_CALL + IDENTIFIER (recursive) FUNCTION(1) + LIST + IDENTIFIER (a) LOCAL_VAR(0) + FUNCTION + IDENTIFIER (recursive) + LIST + IDENTIFIER (x) + BLOCK + LIST + IF_STATEMENT + OPERATOR (==) + IDENTIFIER (x) PARAMETER(0) + NUMBER_LITERAL (0) + RETURN_STATEMENT + NUMBER_LITERAL (1) + LOCAL_VARIABLE + IDENTIFIER (prev) + ASSIGNMENT_STATEMENT + IDENTIFIER (prev) LOCAL_VAR(1) + FUNCTION_CALL + IDENTIFIER (recursive) FUNCTION(1) + LIST + OPERATOR (-) + IDENTIFIER (x) PARAMETER(0) + NUMBER_LITERAL (1) + RETURN_STATEMENT + OPERATOR (+) + OPERATOR (*) + IDENTIFIER (prev) LOCAL_VAR(1) + NUMBER_LITERAL (2) + NUMBER_LITERAL (1) diff --git a/ps5/vsl_programs/ps4-symbols/expected/shadowing.symbols b/ps5/vsl_programs/ps4-symbols/expected/shadowing.symbols new file mode 100644 index 0000000..4bdbbc8 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/expected/shadowing.symbols @@ -0,0 +1,150 @@ +0: GLOBAL_VAR(x) +1: GLOBAL_VAR(y) +2: GLOBAL_VAR(z) +3: FUNCTION(main) + 0: PARAMETER(x) + 1: PARAMETER(y) + 2: LOCAL_VAR(x) + 3: LOCAL_VAR(x) + 4: LOCAL_VAR(y) + 5: LOCAL_VAR(y) +4: FUNCTION(other) + 0: PARAMETER(x) + 1: LOCAL_VAR(z) + 2: LOCAL_VAR(x) + 3: LOCAL_VAR(z) + 4: LOCAL_VAR(x) + + == STRING LIST == + + == BOUND SYNTAX TREE == +LIST + GLOBAL_VARIABLE_DECLARATION + LIST + IDENTIFIER (x) + IDENTIFIER (y) + IDENTIFIER (z) + FUNCTION + IDENTIFIER (main) + LIST + IDENTIFIER (x) + IDENTIFIER (y) + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (x) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) LOCAL_VAR(2) + OPERATOR (+) + IDENTIFIER (y) PARAMETER(1) + NUMBER_LITERAL (2) + IF_STATEMENT + OPERATOR (>) + IDENTIFIER (x) LOCAL_VAR(2) + NUMBER_LITERAL (10) + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (x) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) LOCAL_VAR(3) + NUMBER_LITERAL (6) + LOCAL_VARIABLE + IDENTIFIER (y) + ASSIGNMENT_STATEMENT + IDENTIFIER (y) LOCAL_VAR(4) + NUMBER_LITERAL (2) + ASSIGNMENT_STATEMENT + IDENTIFIER (z) GLOBAL_VAR(2) + OPERATOR (+) + IDENTIFIER (x) LOCAL_VAR(3) + IDENTIFIER (y) LOCAL_VAR(4) + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (y) + ASSIGNMENT_STATEMENT + IDENTIFIER (y) LOCAL_VAR(5) + OPERATOR (+) + NUMBER_LITERAL (20) + IDENTIFIER (x) LOCAL_VAR(3) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) LOCAL_VAR(3) + IDENTIFIER (y) LOCAL_VAR(5) + ASSIGNMENT_STATEMENT + IDENTIFIER (z) GLOBAL_VAR(2) + IDENTIFIER (y) LOCAL_VAR(4) + ASSIGNMENT_STATEMENT + IDENTIFIER (y) PARAMETER(1) + OPERATOR (+) + IDENTIFIER (y) PARAMETER(1) + FUNCTION_CALL + IDENTIFIER (other) FUNCTION(4) + LIST + IDENTIFIER (x) LOCAL_VAR(2) + RETURN_STATEMENT + IDENTIFIER (y) PARAMETER(1) + FUNCTION + IDENTIFIER (other) + LIST + IDENTIFIER (x) + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (z) + ASSIGNMENT_STATEMENT + IDENTIFIER (z) LOCAL_VAR(1) + OPERATOR (+) + IDENTIFIER (x) PARAMETER(0) + NUMBER_LITERAL (1) + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (x) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) LOCAL_VAR(2) + OPERATOR (+) + IDENTIFIER (z) LOCAL_VAR(1) + NUMBER_LITERAL (1) + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (z) + ASSIGNMENT_STATEMENT + IDENTIFIER (z) LOCAL_VAR(3) + OPERATOR (+) + IDENTIFIER (x) LOCAL_VAR(2) + NUMBER_LITERAL (1) + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (x) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) LOCAL_VAR(4) + OPERATOR (+) + IDENTIFIER (z) LOCAL_VAR(3) + NUMBER_LITERAL (1) + BLOCK + LIST + ASSIGNMENT_STATEMENT + IDENTIFIER (z) LOCAL_VAR(3) + OPERATOR (+) + IDENTIFIER (x) LOCAL_VAR(4) + NUMBER_LITERAL (1) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) LOCAL_VAR(2) + OPERATOR (+) + IDENTIFIER (z) LOCAL_VAR(3) + NUMBER_LITERAL (1) + ASSIGNMENT_STATEMENT + IDENTIFIER (z) LOCAL_VAR(1) + OPERATOR (+) + IDENTIFIER (x) LOCAL_VAR(2) + NUMBER_LITERAL (1) + ASSIGNMENT_STATEMENT + IDENTIFIER (x) PARAMETER(0) + OPERATOR (+) + IDENTIFIER (z) LOCAL_VAR(1) + NUMBER_LITERAL (1) + RETURN_STATEMENT + IDENTIFIER (x) PARAMETER(0) diff --git a/ps5/vsl_programs/ps4-symbols/expected/simple-globals.symbols b/ps5/vsl_programs/ps4-symbols/expected/simple-globals.symbols new file mode 100644 index 0000000..c221459 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/expected/simple-globals.symbols @@ -0,0 +1,47 @@ +0: GLOBAL_VAR(a) +1: GLOBAL_VAR(b) +2: FUNCTION(main) +3: FUNCTION(func2) +4: GLOBAL_ARRAY(array) +5: GLOBAL_ARRAY(other) +6: GLOBAL_VAR(x) + + == STRING LIST == + + == BOUND SYNTAX TREE == +LIST + GLOBAL_VARIABLE_DECLARATION + LIST + IDENTIFIER (a) + IDENTIFIER (b) + FUNCTION + IDENTIFIER (main) + LIST + RETURN_STATEMENT + OPERATOR (+) + OPERATOR (+) + IDENTIFIER (a) GLOBAL_VAR(0) + IDENTIFIER (x) GLOBAL_VAR(6) + FUNCTION_CALL + IDENTIFIER (func2) FUNCTION(3) + LIST + FUNCTION + IDENTIFIER (func2) + LIST + RETURN_STATEMENT + ARRAY_INDEXING + IDENTIFIER (array) GLOBAL_ARRAY(4) + ARRAY_INDEXING + IDENTIFIER (other) GLOBAL_ARRAY(5) + NUMBER_LITERAL (5) + GLOBAL_VARIABLE_DECLARATION + LIST + ARRAY_INDEXING + IDENTIFIER (array) + NUMBER_LITERAL (2) + ARRAY_INDEXING + IDENTIFIER (other) + NUMBER_LITERAL (10) + GLOBAL_VARIABLE_DECLARATION + LIST + IDENTIFIER (x) diff --git a/ps5/vsl_programs/ps4-symbols/expected/simple-locals.symbols b/ps5/vsl_programs/ps4-symbols/expected/simple-locals.symbols new file mode 100644 index 0000000..3f0bd97 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/expected/simple-locals.symbols @@ -0,0 +1,61 @@ +0: FUNCTION(main) + 0: LOCAL_VAR(a) + 1: LOCAL_VAR(b) + 2: LOCAL_VAR(c) +1: FUNCTION(other) + 0: PARAMETER(x) + 1: PARAMETER(y) + 2: PARAMETER(z) + + == STRING LIST == + + == BOUND SYNTAX TREE == +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + LOCAL_VARIABLE + IDENTIFIER (a) + LOCAL_VARIABLE + IDENTIFIER (b) + ASSIGNMENT_STATEMENT + IDENTIFIER (b) LOCAL_VAR(1) + NUMBER_LITERAL (2) + LOCAL_VARIABLE + IDENTIFIER (c) + ASSIGNMENT_STATEMENT + IDENTIFIER (a) LOCAL_VAR(0) + NUMBER_LITERAL (6) + IF_STATEMENT + OPERATOR (>) + IDENTIFIER (a) LOCAL_VAR(0) + IDENTIFIER (b) LOCAL_VAR(1) + ASSIGNMENT_STATEMENT + IDENTIFIER (c) LOCAL_VAR(2) + NUMBER_LITERAL (2) + ASSIGNMENT_STATEMENT + IDENTIFIER (c) LOCAL_VAR(2) + NUMBER_LITERAL (6) + RETURN_STATEMENT + FUNCTION_CALL + IDENTIFIER (other) FUNCTION(1) + LIST + IDENTIFIER (a) LOCAL_VAR(0) + IDENTIFIER (b) LOCAL_VAR(1) + IDENTIFIER (c) LOCAL_VAR(2) + FUNCTION + IDENTIFIER (other) + LIST + IDENTIFIER (x) + IDENTIFIER (y) + IDENTIFIER (z) + BLOCK + LIST + RETURN_STATEMENT + OPERATOR (+) + IDENTIFIER (x) PARAMETER(0) + OPERATOR (*) + IDENTIFIER (y) PARAMETER(1) + NUMBER_LITERAL (2) diff --git a/ps5/vsl_programs/ps4-symbols/expected/simple-strings.symbols b/ps5/vsl_programs/ps4-symbols/expected/simple-strings.symbols new file mode 100644 index 0000000..75b27f3 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/expected/simple-strings.symbols @@ -0,0 +1,36 @@ +0: FUNCTION(main) +1: FUNCTION(printNewlines) + + == STRING LIST == +0: "Hello " +1: " World" +2: "Another one" +3: "\n" +4: "\n\n\n" + + == BOUND SYNTAX TREE == +LIST + FUNCTION + IDENTIFIER (main) + LIST + BLOCK + LIST + PRINT_STATEMENT + LIST + STRING_LIST_REFERENCE (0) + NUMBER_LITERAL (2) + STRING_LIST_REFERENCE (1) + BLOCK + LIST + PRINT_STATEMENT + LIST + STRING_LIST_REFERENCE (2) + STRING_LIST_REFERENCE (3) + FUNCTION + IDENTIFIER (printNewlines) + LIST + BLOCK + LIST + PRINT_STATEMENT + LIST + STRING_LIST_REFERENCE (4) diff --git a/ps5/vsl_programs/ps4-symbols/expected/strings.symbols b/ps5/vsl_programs/ps4-symbols/expected/strings.symbols new file mode 100644 index 0000000..92de7a2 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/expected/strings.symbols @@ -0,0 +1,69 @@ +0: GLOBAL_VAR(glob) +1: FUNCTION(a) + + == STRING LIST == +0: "Hei" +1: " " +2: "and" +3: " " +4: "hello" +5: "\n" +6: "Cool" +7: "\n" +8: "Done!" +9: "\n" +10: "Glob is now: " +11: "\n" + + == BOUND SYNTAX TREE == +LIST + GLOBAL_VARIABLE_DECLARATION + LIST + IDENTIFIER (glob) + FUNCTION + IDENTIFIER (a) + LIST + BLOCK + LIST + PRINT_STATEMENT + LIST + STRING_LIST_REFERENCE (0) + STRING_LIST_REFERENCE (1) + STRING_LIST_REFERENCE (2) + STRING_LIST_REFERENCE (3) + STRING_LIST_REFERENCE (4) + STRING_LIST_REFERENCE (5) + ASSIGNMENT_STATEMENT + IDENTIFIER (glob) GLOBAL_VAR(0) + NUMBER_LITERAL (10) + WHILE_STATEMENT + NUMBER_LITERAL (1) + BLOCK + LIST + PRINT_STATEMENT + LIST + STRING_LIST_REFERENCE (6) + STRING_LIST_REFERENCE (7) + IF_STATEMENT + OPERATOR (==) + IDENTIFIER (glob) GLOBAL_VAR(0) + NUMBER_LITERAL (0) + BLOCK + LIST + PRINT_STATEMENT + LIST + STRING_LIST_REFERENCE (8) + STRING_LIST_REFERENCE (9) + BREAK_STATEMENT + ASSIGNMENT_STATEMENT + IDENTIFIER (glob) GLOBAL_VAR(0) + OPERATOR (-) + IDENTIFIER (glob) GLOBAL_VAR(0) + NUMBER_LITERAL (1) + PRINT_STATEMENT + LIST + STRING_LIST_REFERENCE (10) + IDENTIFIER (glob) GLOBAL_VAR(0) + STRING_LIST_REFERENCE (11) + RETURN_STATEMENT + NUMBER_LITERAL (5) diff --git a/ps5/vsl_programs/ps4-symbols/locals.vsl b/ps5/vsl_programs/ps4-symbols/locals.vsl new file mode 100644 index 0000000..9e93a49 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/locals.vsl @@ -0,0 +1,28 @@ +func main() { + var a = 2, b = 6 + + while (a + b < 10) { + a = a + 1 + + var tmp = a + a = b + b = tmp + } + + if (a < b) { + var tmp + tmp = a + a = b + b = tmp + } + + return recursive(a) +} + +func recursive(x) { + if (x == 0) + return 1 + + var prev = recursive(x - 1) + return prev * 2 + 1 +} diff --git a/ps5/vsl_programs/ps4-symbols/shadowing.vsl b/ps5/vsl_programs/ps4-symbols/shadowing.vsl new file mode 100644 index 0000000..0d46043 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/shadowing.vsl @@ -0,0 +1,39 @@ + +var x, y, z + +func main(x, y) { + var x = y + 2 + if (x > 10) { + var x = 6, y = 2 + z = x + y + { + var y = 20 + x + x = y + } + z = y + } + + y = y + other(x) + + return y +} + +func other(x) { + var z = x + 1 + { + var x = z + 1 + { + var z = x + 1 + { + var x = z + 1 + { + z = x + 1 + } + } + x = z + 1 + } + z = x + 1 + } + x = z + 1 + return x +} diff --git a/ps5/vsl_programs/ps4-symbols/simple-globals.vsl b/ps5/vsl_programs/ps4-symbols/simple-globals.vsl new file mode 100644 index 0000000..155c1c6 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/simple-globals.vsl @@ -0,0 +1,10 @@ +var a, b + +func main() + return a + x + func2() + +func func2() + return array[other[5]] + +var array[2], other[10] +var x diff --git a/ps5/vsl_programs/ps4-symbols/simple-locals.vsl b/ps5/vsl_programs/ps4-symbols/simple-locals.vsl new file mode 100644 index 0000000..ecb4920 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/simple-locals.vsl @@ -0,0 +1,17 @@ +func main() { + var a, b = 2 + var c + + a = 6 + + if (a > b) + c = 2 + else + c = 6 + + return other(a, b, c) +} + +func other(x, y, z) { + return x + y * 2 +} diff --git a/ps5/vsl_programs/ps4-symbols/simple-strings.vsl b/ps5/vsl_programs/ps4-symbols/simple-strings.vsl new file mode 100644 index 0000000..19d5184 --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/simple-strings.vsl @@ -0,0 +1,10 @@ +func main() { + print("Hello ", 2, " World") + { + println("Another one") + } +} + +func printNewlines() { + print("\n\n\n") +} diff --git a/ps5/vsl_programs/ps4-symbols/strings.vsl b/ps5/vsl_programs/ps4-symbols/strings.vsl new file mode 100644 index 0000000..e2722de --- /dev/null +++ b/ps5/vsl_programs/ps4-symbols/strings.vsl @@ -0,0 +1,18 @@ +var glob + +func a() { + println("Hei", " ", "and", " ", "hello") + + glob = 10 + while (1) { + println("Cool") + if (glob == 0) { + println("Done!") + break + } + glob = glob - 1 + println("Glob is now: ", glob) + } + + return 5 +} diff --git a/ps5/vsl_programs/ps5-codegen1/arrays.vsl b/ps5/vsl_programs/ps5-codegen1/arrays.vsl new file mode 100644 index 0000000..d49faac --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/arrays.vsl @@ -0,0 +1,22 @@ +var scalar, array[10] + +func main() { + array[0] = 1 + array[1] = array[0] + 1 + array[array[1]] = 3 + + println("The values of the array are: ", array[0], " ", array[1], " ", array[2], " ", array[3]) + + array[3] = 1 + + array[sum(array[0], array[1])] = sum(array[2], array[array[2]]) + + println("array[3] = ", array[3]) +} + +func sum(a, b) + return a + b + +//TESTCASE: +//The values of the array are: 1 2 3 0 +//array[3] = 4 diff --git a/ps5/vsl_programs/ps5-codegen1/callframes.vsl b/ps5/vsl_programs/ps5-codegen1/callframes.vsl new file mode 100644 index 0000000..832cbe2 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/callframes.vsl @@ -0,0 +1,30 @@ + +func main(first, last) { + println("Printing the sum of all integers from ", first, " up to, but not including ", last) + var result = sumRange(first, last) + println(result) +} + +// Calculates the sum of the numbers +// first, first+1, first+2 ... last-2, last-1 +func sumRange(first, last) { + var sumEnd, sumPreFirst + sumEnd = sumUntil(last) + sumPreFirst = sumUntil(first) + return sumEnd - sumPreFirst +} + +// Calculates the sum of 1, 2, 3, ..., n-2, n-1 +func sumUntil(n) { + var product = n * (n-1) + product = product / 2 + return product +} + +//TESTCASE: 2 10 +//Printing the sum of all integers from 2 up to, but not including 10 +//44 + +//TESTCASE: 6 6 +//Printing the sum of all integers from 6 up to, but not including 6 +//0 diff --git a/ps5/vsl_programs/ps5-codegen1/comparisons.vsl b/ps5/vsl_programs/ps5-codegen1/comparisons.vsl new file mode 100644 index 0000000..994b09f --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/comparisons.vsl @@ -0,0 +1,72 @@ +func start(a) { + println("a = ", a) + println("a < 8: ", a < 8) + println("a < 5: ", a < 5) + println("a <= 5: ", a <= 5) + println("a == 5: ", a == 5) + println("a != 5: ", a != 5) + println("!(a == 5): ", !(a == 5)) + println("a >= 5: ", a >= 5) + println("a > 5: ", a > 5) + println("a > -8: ", a > -8) +} + +//TESTCASE: 5 +//a = 5 +//a < 8: 1 +//a < 5: 0 +//a <= 5: 1 +//a == 5: 1 +//a != 5: 0 +//!(a == 5): 0 +//a >= 5: 1 +//a > 5: 0 +//a > -8: 1 + +//TESTCASE: 4 +//a = 4 +//a < 8: 1 +//a < 5: 1 +//a <= 5: 1 +//a == 5: 0 +//a != 5: 1 +//!(a == 5): 1 +//a >= 5: 0 +//a > 5: 0 +//a > -8: 1 + +//TESTCASE: 6 +//a = 6 +//a < 8: 1 +//a < 5: 0 +//a <= 5: 0 +//a == 5: 0 +//a != 5: 1 +//!(a == 5): 1 +//a >= 5: 1 +//a > 5: 1 +//a > -8: 1 + +//TESTCASE: -8 +//a = -8 +//a < 8: 1 +//a < 5: 1 +//a <= 5: 1 +//a == 5: 0 +//a != 5: 1 +//!(a == 5): 1 +//a >= 5: 0 +//a > 5: 0 +//a > -8: 0 + +//TESTCASE: 8 +//a = 8 +//a < 8: 0 +//a < 5: 0 +//a <= 5: 0 +//a == 5: 0 +//a != 5: 1 +//!(a == 5): 1 +//a >= 5: 1 +//a > 5: 1 +//a > -8: 1 diff --git a/ps5/vsl_programs/ps5-codegen1/everything.vsl b/ps5/vsl_programs/ps5-codegen1/everything.vsl new file mode 100644 index 0000000..9e48cc5 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/everything.vsl @@ -0,0 +1,26 @@ + +var X, Y, array[10] + +func main(a, b) { + X = a + Y = b + + array[0] = mulAndAdd(a/a, 1000) + swap() + array[1] = mulAndAdd(1, 1000) + print(array[0], " ") + println(array[1]) +} + +func mulAndAdd(x, y) { + return x * X + y * Y +} + +func swap() { + var tmp = X + X = Y + Y = tmp +} + +//TESTCASE: 4 6 +//6004 4006 diff --git a/ps5/vsl_programs/ps5-codegen1/globals.vsl b/ps5/vsl_programs/ps5-codegen1/globals.vsl new file mode 100644 index 0000000..30422a2 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/globals.vsl @@ -0,0 +1,19 @@ +var A, B +var dummy + +func main() { + println("On startup, A and B are ", A, " ", B) + A = 5 + otherFunc() + println("Now, B is ", B) +} + +func otherFunc() { + println("Here, A is ", A) + B = 2 +} + +//TESTCASE: +//On startup, A and B are 0 0 +//Here, A is 5 +//Now, B is 2 diff --git a/ps5/vsl_programs/ps5-codegen1/locals.vsl b/ps5/vsl_programs/ps5-codegen1/locals.vsl new file mode 100644 index 0000000..362b6e9 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/locals.vsl @@ -0,0 +1,20 @@ +func main() { + var a, b + var c + print("Now, a b c are ") + print(a, " ") + print(b, " ") + println(c) + a = 6 + { + var c = 100 + b = 20 + println("In here, a b c are ", a, " ", b, " ", c) + } + println("Out here, a b c are ", a, " ", b, " ", c) +} + +//TESTCASE: +//Now, a b c are 0 0 0 +//In here, a b c are 6 20 100 +//Out here, a b c are 6 20 0 diff --git a/ps5/vsl_programs/ps5-codegen1/many-params.vsl b/ps5/vsl_programs/ps5-codegen1/many-params.vsl new file mode 100644 index 0000000..8a8e7b7 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/many-params.vsl @@ -0,0 +1,27 @@ +func main(a, b, c, d, e, f, g, h) { + var sumPlus5, alsoSumPlus5 + println("Inside main, the arguments are: ", a, " ", b, " ", c, " ", d, " ", e, " ", f, " ", g, " ", h) + sumPlus5 = 5 + otherFunc(a, b, c, d, e, f, g, h) + println("Sum plus 5: ", sumPlus5) + alsoSumPlus5 = otherFunc(a+1, b, c+1, d, e+1, f, g+2, h) + println("Also sum plus 5: ", alsoSumPlus5) + println("At the end of main, the arguments are: ", a, " ", b, " ", c, " ", d, " ", e, " ", f, " ", g, " ", h) +} + +func otherFunc(a, b, c, d, e, f, g, h) { + var sum = a + b + c + d + println("Inside otherFunc, the arguments are: ", a, " ", b, " ", c, " ", d, " ", e, " ", f, " ", g, " ", h) + sum = sum + e + f + g + h + return sum +} + +//TESTCASE: 1 3 5 7 9 +//Wrong number of arguments + +//TESTCASE: 1 3 5 7 9 11 13 15 +//Inside main, the arguments are: 1 3 5 7 9 11 13 15 +//Inside otherFunc, the arguments are: 1 3 5 7 9 11 13 15 +//Sum plus 5: 69 +//Inside otherFunc, the arguments are: 2 3 6 7 10 11 15 15 +//Also sum plus 5: 69 +//At the end of main, the arguments are: 1 3 5 7 9 11 13 15 diff --git a/ps5/vsl_programs/ps5-codegen1/math.vsl b/ps5/vsl_programs/ps5-codegen1/math.vsl new file mode 100644 index 0000000..84c9282 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/math.vsl @@ -0,0 +1,16 @@ +func main(num) { + println("num = ", num) + println("num*3 = ", num*3) + println("num+5 = ", num+5) + println("num/5 = ", num/5) + println("4*-num = ", 4*-num) + println("(num-1)*(num+1)*(num+3) = ", (num-1)*(num+1)*(num+3)) +} + +//TESTCASE: 12 +//num = 12 +//num*3 = 36 +//num+5 = 17 +//num/5 = 2 +//4*-num = -48 +//(num-1)*(num+1)*(num+3) = 2145 diff --git a/ps5/vsl_programs/ps5-codegen1/simple-array.vsl b/ps5/vsl_programs/ps5-codegen1/simple-array.vsl new file mode 100644 index 0000000..1a77d48 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/simple-array.vsl @@ -0,0 +1,11 @@ +var myArray[10] + +func main() { + myArray[1] = 5 + println(myArray[0]) + println(myArray[1]) +} + +//TESTCASE: +//0 +//5 diff --git a/ps5/vsl_programs/ps5-codegen1/simple-call.vsl b/ps5/vsl_programs/ps5-codegen1/simple-call.vsl new file mode 100644 index 0000000..147ba62 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/simple-call.vsl @@ -0,0 +1,14 @@ +func main(x) { + println(addOne(x)) +} + +func addOne(x) { + return x + 1 +} + +//TESTCASE: -5 +//-4 + +//TESTCASE: 1000 +//1001 + diff --git a/ps5/vsl_programs/ps5-codegen1/simple-locals.vsl b/ps5/vsl_programs/ps5-codegen1/simple-locals.vsl new file mode 100644 index 0000000..c275085 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/simple-locals.vsl @@ -0,0 +1,10 @@ +func main() { + var A, B = 20 + var C + A = 10 + C = A + B + println(C) +} + +//TESTCASE: +//30 diff --git a/ps5/vsl_programs/ps5-codegen1/simple-parameters.vsl b/ps5/vsl_programs/ps5-codegen1/simple-parameters.vsl new file mode 100644 index 0000000..0252fd0 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/simple-parameters.vsl @@ -0,0 +1,10 @@ +func printSum(a, b) { + println("sum: ", a+b) +} + +//TESTCASE: 100 200 +//sum: 300 + +// Sum too large to fit in int32 +//TESTCASE: 2000000000 1000000000 +//sum: 3000000000 diff --git a/ps5/vsl_programs/ps5-codegen1/simple-print.vsl b/ps5/vsl_programs/ps5-codegen1/simple-print.vsl new file mode 100644 index 0000000..dce3bb6 --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/simple-print.vsl @@ -0,0 +1,9 @@ +func main() { + println("Hello, World!") +} + +//TESTCASE: +//Hello, World! + +//TESTCASE: 5 +//Wrong number of arguments diff --git a/ps5/vsl_programs/ps5-codegen1/simple-return.vsl b/ps5/vsl_programs/ps5-codegen1/simple-return.vsl new file mode 100644 index 0000000..f101c4b --- /dev/null +++ b/ps5/vsl_programs/ps5-codegen1/simple-return.vsl @@ -0,0 +1,24 @@ + +func main() +{ + println("zero: ", zero()) + println("one: ", one()) +} + +func zero() +{ + // Do some math to mess with the %rax register + var a = 5 + var b = a + 7 + + // We should return 0 since to return statement is given +} + +func one() +{ + return 1 +} + +//TESTCASE: +//zero: 0 +//one: 1