ps5: init
This commit is contained in:
14
ps5/.gitignore
vendored
Normal file
14
ps5/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
*.log
|
||||
tmp/
|
||||
.cache/
|
||||
|
||||
build/
|
||||
|
||||
*.ast
|
||||
*.svg
|
||||
*.symbols
|
||||
*.S
|
||||
*.out
|
||||
!vsl_programs/*/expected/*
|
||||
53
ps5/CMakeLists.txt
Normal file
53
ps5/CMakeLists.txt
Normal file
@@ -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()
|
||||
94
ps5/src/emit.h
Normal file
94
ps5/src/emit.h
Normal file
@@ -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_
|
||||
225
ps5/src/generator.c
Normal file
225
ps5/src/generator.c
Normal file
@@ -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 ( <entry point function symbol> );
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
71
ps5/src/graphviz_output.c
Normal file
71
ps5/src/graphviz_output.c
Normal file
@@ -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");
|
||||
}
|
||||
33
ps5/src/nodetypes.h
Normal file
33
ps5/src/nodetypes.h
Normal file
@@ -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
|
||||
262
ps5/src/parser.y
Normal file
262
ps5/src/parser.y
Normal file
@@ -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);
|
||||
}
|
||||
%%
|
||||
38
ps5/src/scanner.l
Normal file
38
ps5/src/scanner.l
Normal file
@@ -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]; }
|
||||
%%
|
||||
168
ps5/src/symbol_table.c
Normal file
168
ps5/src/symbol_table.c
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "symbol_table.h"
|
||||
#include "symbols.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
64
ps5/src/symbol_table.h
Normal file
64
ps5/src/symbol_table.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef SYMBOL_TABLE_H
|
||||
#define SYMBOL_TABLE_H
|
||||
|
||||
#include "tree.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// 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
|
||||
294
ps5/src/symbols.c
Normal file
294
ps5/src/symbols.c
Normal file
@@ -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);
|
||||
}
|
||||
59
ps5/src/symbols.h
Normal file
59
ps5/src/symbols.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef SYMBOLS_H
|
||||
#define SYMBOLS_H
|
||||
|
||||
#include "symbol_table.h"
|
||||
#include <stddef.h>
|
||||
|
||||
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
|
||||
385
ps5/src/tree.c
Normal file
385
ps5/src/tree.c
Normal file
@@ -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"
|
||||
};
|
||||
64
ps5/src/tree.h
Normal file
64
ps5/src/tree.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef TREE_H
|
||||
#define TREE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// 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
|
||||
84
ps5/src/vslc.c
Normal file
84
ps5/src/vslc.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "vslc.h"
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
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
|
||||
}
|
||||
26
ps5/src/vslc.h
Normal file
26
ps5/src/vslc.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef VSLC_H
|
||||
#define VSLC_H
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 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
|
||||
78
ps5/vsl_programs/codegen-tester.py
Executable file
78
ps5/vsl_programs/codegen-tester.py
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import subprocess
|
||||
import os.path
|
||||
|
||||
name, *args = sys.argv
|
||||
|
||||
USAGE = f"""
|
||||
Usage: {name} <file.vsl>
|
||||
|
||||
For each occurance of a VSL comment block starting with
|
||||
//TESTCASE: <args>
|
||||
The corresponding compiler executable file.out is executed with the given <args>.
|
||||
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))
|
||||
14
ps5/vsl_programs/ps2-parser/arrays.vsl
Normal file
14
ps5/vsl_programs/ps2-parser/arrays.vsl
Normal file
@@ -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])
|
||||
}
|
||||
63
ps5/vsl_programs/ps2-parser/expected/arrays.ast
Normal file
63
ps5/vsl_programs/ps2-parser/expected/arrays.ast
Normal file
@@ -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)
|
||||
60
ps5/vsl_programs/ps2-parser/expected/if.ast
Normal file
60
ps5/vsl_programs/ps2-parser/expected/if.ast
Normal file
@@ -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")
|
||||
146
ps5/vsl_programs/ps2-parser/expected/operators.ast
Normal file
146
ps5/vsl_programs/ps2-parser/expected/operators.ast
Normal file
@@ -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)
|
||||
25
ps5/vsl_programs/ps2-parser/expected/simple-assignment.ast
Normal file
25
ps5/vsl_programs/ps2-parser/expected/simple-assignment.ast
Normal file
@@ -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)
|
||||
51
ps5/vsl_programs/ps2-parser/expected/simple-functions.ast
Normal file
51
ps5/vsl_programs/ps2-parser/expected/simple-functions.ast
Normal file
@@ -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)
|
||||
12
ps5/vsl_programs/ps2-parser/expected/simple-hello.ast
Normal file
12
ps5/vsl_programs/ps2-parser/expected/simple-hello.ast
Normal file
@@ -0,0 +1,12 @@
|
||||
LIST
|
||||
FUNCTION
|
||||
IDENTIFIER (main)
|
||||
LIST
|
||||
BLOCK
|
||||
LIST
|
||||
PRINT_STATEMENT
|
||||
LIST
|
||||
STRING_LITERAL ("Hello ")
|
||||
PRINTLN_STATEMENT
|
||||
LIST
|
||||
STRING_LITERAL ("World!")
|
||||
88
ps5/vsl_programs/ps2-parser/expected/variables.ast
Normal file
88
ps5/vsl_programs/ps2-parser/expected/variables.ast
Normal file
@@ -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)
|
||||
56
ps5/vsl_programs/ps2-parser/expected/while.ast
Normal file
56
ps5/vsl_programs/ps2-parser/expected/while.ast
Normal file
@@ -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)
|
||||
27
ps5/vsl_programs/ps2-parser/if.vsl
Normal file
27
ps5/vsl_programs/ps2-parser/if.vsl
Normal file
@@ -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")
|
||||
}
|
||||
34
ps5/vsl_programs/ps2-parser/operators.vsl
Normal file
34
ps5/vsl_programs/ps2-parser/operators.vsl
Normal file
@@ -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
|
||||
}
|
||||
12
ps5/vsl_programs/ps2-parser/simple-assignment.vsl
Normal file
12
ps5/vsl_programs/ps2-parser/simple-assignment.vsl
Normal file
@@ -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
|
||||
16
ps5/vsl_programs/ps2-parser/simple-functions.vsl
Normal file
16
ps5/vsl_programs/ps2-parser/simple-functions.vsl
Normal file
@@ -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
|
||||
}
|
||||
5
ps5/vsl_programs/ps2-parser/simple-hello.vsl
Normal file
5
ps5/vsl_programs/ps2-parser/simple-hello.vsl
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
func main() {
|
||||
print("Hello ")
|
||||
println("World!")
|
||||
}
|
||||
30
ps5/vsl_programs/ps2-parser/variables.vsl
Normal file
30
ps5/vsl_programs/ps2-parser/variables.vsl
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
20
ps5/vsl_programs/ps2-parser/while.vsl
Normal file
20
ps5/vsl_programs/ps2-parser/while.vsl
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
14
ps5/vsl_programs/ps3-simplify/and-or-convert.vsl
Normal file
14
ps5/vsl_programs/ps3-simplify/and-or-convert.vsl
Normal file
@@ -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")
|
||||
}
|
||||
26
ps5/vsl_programs/ps3-simplify/constant-fold.vsl
Normal file
26
ps5/vsl_programs/ps3-simplify/constant-fold.vsl
Normal file
@@ -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
|
||||
}
|
||||
55
ps5/vsl_programs/ps3-simplify/expected/and-or-convert.ast
Normal file
55
ps5/vsl_programs/ps3-simplify/expected/and-or-convert.ast
Normal file
@@ -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")
|
||||
71
ps5/vsl_programs/ps3-simplify/expected/constant-fold.ast
Normal file
71
ps5/vsl_programs/ps3-simplify/expected/constant-fold.ast
Normal file
@@ -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)
|
||||
21
ps5/vsl_programs/ps3-simplify/expected/println-convert.ast
Normal file
21
ps5/vsl_programs/ps3-simplify/expected/println-convert.ast
Normal file
@@ -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")
|
||||
29
ps5/vsl_programs/ps3-simplify/expected/var-convert.ast
Normal file
29
ps5/vsl_programs/ps3-simplify/expected/var-convert.ast
Normal file
@@ -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)
|
||||
6
ps5/vsl_programs/ps3-simplify/println-convert.vsl
Normal file
6
ps5/vsl_programs/ps3-simplify/println-convert.vsl
Normal file
@@ -0,0 +1,6 @@
|
||||
func main() {
|
||||
print(20, " and ", 40)
|
||||
println("!")
|
||||
|
||||
println(50, " and ", 50)
|
||||
}
|
||||
10
ps5/vsl_programs/ps3-simplify/var-convert.vsl
Normal file
10
ps5/vsl_programs/ps3-simplify/var-convert.vsl
Normal file
@@ -0,0 +1,10 @@
|
||||
func main() {
|
||||
var a, b = 2, c = b
|
||||
|
||||
print(b)
|
||||
|
||||
var c
|
||||
c = 10
|
||||
|
||||
print(c)
|
||||
}
|
||||
102
ps5/vsl_programs/ps4-symbols/expected/locals.symbols
Normal file
102
ps5/vsl_programs/ps4-symbols/expected/locals.symbols
Normal file
@@ -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)
|
||||
150
ps5/vsl_programs/ps4-symbols/expected/shadowing.symbols
Normal file
150
ps5/vsl_programs/ps4-symbols/expected/shadowing.symbols
Normal file
@@ -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)
|
||||
47
ps5/vsl_programs/ps4-symbols/expected/simple-globals.symbols
Normal file
47
ps5/vsl_programs/ps4-symbols/expected/simple-globals.symbols
Normal file
@@ -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)
|
||||
61
ps5/vsl_programs/ps4-symbols/expected/simple-locals.symbols
Normal file
61
ps5/vsl_programs/ps4-symbols/expected/simple-locals.symbols
Normal file
@@ -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)
|
||||
36
ps5/vsl_programs/ps4-symbols/expected/simple-strings.symbols
Normal file
36
ps5/vsl_programs/ps4-symbols/expected/simple-strings.symbols
Normal file
@@ -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)
|
||||
69
ps5/vsl_programs/ps4-symbols/expected/strings.symbols
Normal file
69
ps5/vsl_programs/ps4-symbols/expected/strings.symbols
Normal file
@@ -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)
|
||||
28
ps5/vsl_programs/ps4-symbols/locals.vsl
Normal file
28
ps5/vsl_programs/ps4-symbols/locals.vsl
Normal file
@@ -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
|
||||
}
|
||||
39
ps5/vsl_programs/ps4-symbols/shadowing.vsl
Normal file
39
ps5/vsl_programs/ps4-symbols/shadowing.vsl
Normal file
@@ -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
|
||||
}
|
||||
10
ps5/vsl_programs/ps4-symbols/simple-globals.vsl
Normal file
10
ps5/vsl_programs/ps4-symbols/simple-globals.vsl
Normal file
@@ -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
|
||||
17
ps5/vsl_programs/ps4-symbols/simple-locals.vsl
Normal file
17
ps5/vsl_programs/ps4-symbols/simple-locals.vsl
Normal file
@@ -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
|
||||
}
|
||||
10
ps5/vsl_programs/ps4-symbols/simple-strings.vsl
Normal file
10
ps5/vsl_programs/ps4-symbols/simple-strings.vsl
Normal file
@@ -0,0 +1,10 @@
|
||||
func main() {
|
||||
print("Hello ", 2, " World")
|
||||
{
|
||||
println("Another one")
|
||||
}
|
||||
}
|
||||
|
||||
func printNewlines() {
|
||||
print("\n\n\n")
|
||||
}
|
||||
18
ps5/vsl_programs/ps4-symbols/strings.vsl
Normal file
18
ps5/vsl_programs/ps4-symbols/strings.vsl
Normal file
@@ -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
|
||||
}
|
||||
22
ps5/vsl_programs/ps5-codegen1/arrays.vsl
Normal file
22
ps5/vsl_programs/ps5-codegen1/arrays.vsl
Normal file
@@ -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
|
||||
30
ps5/vsl_programs/ps5-codegen1/callframes.vsl
Normal file
30
ps5/vsl_programs/ps5-codegen1/callframes.vsl
Normal file
@@ -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
|
||||
72
ps5/vsl_programs/ps5-codegen1/comparisons.vsl
Normal file
72
ps5/vsl_programs/ps5-codegen1/comparisons.vsl
Normal file
@@ -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
|
||||
26
ps5/vsl_programs/ps5-codegen1/everything.vsl
Normal file
26
ps5/vsl_programs/ps5-codegen1/everything.vsl
Normal file
@@ -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
|
||||
19
ps5/vsl_programs/ps5-codegen1/globals.vsl
Normal file
19
ps5/vsl_programs/ps5-codegen1/globals.vsl
Normal file
@@ -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
|
||||
20
ps5/vsl_programs/ps5-codegen1/locals.vsl
Normal file
20
ps5/vsl_programs/ps5-codegen1/locals.vsl
Normal file
@@ -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
|
||||
27
ps5/vsl_programs/ps5-codegen1/many-params.vsl
Normal file
27
ps5/vsl_programs/ps5-codegen1/many-params.vsl
Normal file
@@ -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
|
||||
16
ps5/vsl_programs/ps5-codegen1/math.vsl
Normal file
16
ps5/vsl_programs/ps5-codegen1/math.vsl
Normal file
@@ -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
|
||||
11
ps5/vsl_programs/ps5-codegen1/simple-array.vsl
Normal file
11
ps5/vsl_programs/ps5-codegen1/simple-array.vsl
Normal file
@@ -0,0 +1,11 @@
|
||||
var myArray[10]
|
||||
|
||||
func main() {
|
||||
myArray[1] = 5
|
||||
println(myArray[0])
|
||||
println(myArray[1])
|
||||
}
|
||||
|
||||
//TESTCASE:
|
||||
//0
|
||||
//5
|
||||
14
ps5/vsl_programs/ps5-codegen1/simple-call.vsl
Normal file
14
ps5/vsl_programs/ps5-codegen1/simple-call.vsl
Normal file
@@ -0,0 +1,14 @@
|
||||
func main(x) {
|
||||
println(addOne(x))
|
||||
}
|
||||
|
||||
func addOne(x) {
|
||||
return x + 1
|
||||
}
|
||||
|
||||
//TESTCASE: -5
|
||||
//-4
|
||||
|
||||
//TESTCASE: 1000
|
||||
//1001
|
||||
|
||||
10
ps5/vsl_programs/ps5-codegen1/simple-locals.vsl
Normal file
10
ps5/vsl_programs/ps5-codegen1/simple-locals.vsl
Normal file
@@ -0,0 +1,10 @@
|
||||
func main() {
|
||||
var A, B = 20
|
||||
var C
|
||||
A = 10
|
||||
C = A + B
|
||||
println(C)
|
||||
}
|
||||
|
||||
//TESTCASE:
|
||||
//30
|
||||
10
ps5/vsl_programs/ps5-codegen1/simple-parameters.vsl
Normal file
10
ps5/vsl_programs/ps5-codegen1/simple-parameters.vsl
Normal file
@@ -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
|
||||
9
ps5/vsl_programs/ps5-codegen1/simple-print.vsl
Normal file
9
ps5/vsl_programs/ps5-codegen1/simple-print.vsl
Normal file
@@ -0,0 +1,9 @@
|
||||
func main() {
|
||||
println("Hello, World!")
|
||||
}
|
||||
|
||||
//TESTCASE:
|
||||
//Hello, World!
|
||||
|
||||
//TESTCASE: 5
|
||||
//Wrong number of arguments
|
||||
24
ps5/vsl_programs/ps5-codegen1/simple-return.vsl
Normal file
24
ps5/vsl_programs/ps5-codegen1/simple-return.vsl
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user