├── tests ├── fail │ ├── double_downcast.crema │ ├── func_not_def.crema │ ├── var_decl_illegal_lhs.crema │ ├── var_decl_mismatch.crema │ ├── list_mixed_type.crema │ ├── var_decl_dup.crema │ ├── func_no_return.crema │ ├── func_duplicate_name_arg.crema │ ├── func_recursion.crema │ ├── func_duplicate_args.crema │ ├── func_type_mismatch.crema │ ├── list_invalid_index.crema │ ├── struct_duplicate_members.crema │ ├── duplicate_struct_member.crema │ ├── loop_no_list.crema │ ├── loop_non_list.crema │ ├── func_arg_type_mismatch.crema │ ├── func_arg_num_mismatch.crema │ ├── func_corecursion.crema │ ├── func_nested.crema │ ├── indirect_recursion.crema │ └── recursion.crema ├── success │ ├── var_decl.crema │ ├── bool_array.crema │ ├── char_array.crema │ ├── int_array.crema │ ├── int_upcast.crema │ ├── null_program.crema │ ├── uint_decl.crema │ ├── bool_and_op.crema │ ├── bool_or_op.crema │ ├── bool_var_decl.crema │ ├── double_array.crema │ ├── string_array.crema │ ├── bool_bit_and_op.crema │ ├── bool_bit_or_op.crema │ ├── bool_equality_op.crema │ ├── var_paren_parse.crema │ ├── var_rename.crema │ ├── bool_nequality_op.crema │ ├── expression_upcast.crema │ ├── var_decl_unary_minus.crema │ ├── if_stmt.crema │ ├── struct_decl.crema │ ├── list_binary_op.crema │ ├── if_else_stmt.crema │ ├── else_if_stmt.crema │ ├── func_call.crema │ ├── loop_add.crema │ ├── func_returning_array.crema │ ├── parens_in_expressions.crema │ ├── struct_member.crema │ ├── func_passing_array.crema │ ├── nested_loops.crema │ ├── int_operations.crema │ ├── loop_in_if.crema │ ├── double_operations.crema │ ├── loop_in_if_else.crema │ └── if_in_nested_loops.crema └── run_tests.sh ├── contributors.txt ├── src ├── mainpage.dox ├── decls.h ├── Makefile ├── semantics.h ├── codegen.h ├── types.h ├── lexer.l ├── stdlib │ ├── stdlib.h │ └── stdlib.c ├── crema.cpp ├── types.cpp ├── parser.y ├── ast.h ├── ast.cpp ├── semantics.cpp └── codegen.cpp ├── vagrant ├── provision.sh ├── Vagrant_README.txt └── Vagrantfile ├── LICENSE └── README.md /tests/fail/double_downcast.crema: -------------------------------------------------------------------------------- 1 | int a = 3.4 -------------------------------------------------------------------------------- /tests/success/var_decl.crema: -------------------------------------------------------------------------------- 1 | int a = 32 2 | -------------------------------------------------------------------------------- /tests/fail/func_not_def.crema: -------------------------------------------------------------------------------- 1 | int foo = bar() -------------------------------------------------------------------------------- /tests/success/bool_array.crema: -------------------------------------------------------------------------------- 1 | bool array[] 2 | -------------------------------------------------------------------------------- /tests/success/char_array.crema: -------------------------------------------------------------------------------- 1 | char array[] 2 | -------------------------------------------------------------------------------- /tests/success/int_array.crema: -------------------------------------------------------------------------------- 1 | int array[] 2 | -------------------------------------------------------------------------------- /tests/success/int_upcast.crema: -------------------------------------------------------------------------------- 1 | double b = 3 2 | -------------------------------------------------------------------------------- /tests/success/null_program.crema: -------------------------------------------------------------------------------- 1 | # Nothing to do -------------------------------------------------------------------------------- /tests/success/uint_decl.crema: -------------------------------------------------------------------------------- 1 | uint a = 2 2 | -------------------------------------------------------------------------------- /tests/fail/var_decl_illegal_lhs.crema: -------------------------------------------------------------------------------- 1 | int a b=3 2 | -------------------------------------------------------------------------------- /tests/fail/var_decl_mismatch.crema: -------------------------------------------------------------------------------- 1 | int a = 32.2 2 | -------------------------------------------------------------------------------- /tests/success/bool_and_op.crema: -------------------------------------------------------------------------------- 1 | bool b = true && true -------------------------------------------------------------------------------- /tests/success/bool_or_op.crema: -------------------------------------------------------------------------------- 1 | bool b = true || false -------------------------------------------------------------------------------- /tests/success/bool_var_decl.crema: -------------------------------------------------------------------------------- 1 | bool a = true 2 | -------------------------------------------------------------------------------- /tests/success/double_array.crema: -------------------------------------------------------------------------------- 1 | double array[] 2 | -------------------------------------------------------------------------------- /tests/success/string_array.crema: -------------------------------------------------------------------------------- 1 | string array[] 2 | -------------------------------------------------------------------------------- /tests/success/bool_bit_and_op.crema: -------------------------------------------------------------------------------- 1 | bool b = true & true -------------------------------------------------------------------------------- /tests/success/bool_bit_or_op.crema: -------------------------------------------------------------------------------- 1 | bool b = true | false -------------------------------------------------------------------------------- /tests/success/bool_equality_op.crema: -------------------------------------------------------------------------------- 1 | bool b = true == true -------------------------------------------------------------------------------- /tests/success/var_paren_parse.crema: -------------------------------------------------------------------------------- 1 | int a = (5+6)-4 2 | -------------------------------------------------------------------------------- /tests/success/var_rename.crema: -------------------------------------------------------------------------------- 1 | int a = 5 2 | int b = a 3 | -------------------------------------------------------------------------------- /tests/fail/list_mixed_type.crema: -------------------------------------------------------------------------------- 1 | int a[] = [1, 2.3, "foo"] 2 | -------------------------------------------------------------------------------- /tests/fail/var_decl_dup.crema: -------------------------------------------------------------------------------- 1 | int a = 32 2 | int a = 33 3 | -------------------------------------------------------------------------------- /tests/success/bool_nequality_op.crema: -------------------------------------------------------------------------------- 1 | bool b = true != false -------------------------------------------------------------------------------- /tests/success/expression_upcast.crema: -------------------------------------------------------------------------------- 1 | double a = 3 + 4.5 2 | -------------------------------------------------------------------------------- /tests/success/var_decl_unary_minus.crema: -------------------------------------------------------------------------------- 1 | int a = 10 - (-5) 2 | -------------------------------------------------------------------------------- /tests/fail/func_no_return.crema: -------------------------------------------------------------------------------- 1 | def int foo() { 2 | int a = 2 3 | } 4 | -------------------------------------------------------------------------------- /tests/fail/func_duplicate_name_arg.crema: -------------------------------------------------------------------------------- 1 | def int foo( double foo ) { 2 | } 3 | -------------------------------------------------------------------------------- /tests/fail/func_recursion.crema: -------------------------------------------------------------------------------- 1 | def int foo() { 2 | return foo() 3 | } 4 | -------------------------------------------------------------------------------- /tests/fail/func_duplicate_args.crema: -------------------------------------------------------------------------------- 1 | def int foo( int a, double a ) { 2 | } 3 | -------------------------------------------------------------------------------- /tests/fail/func_type_mismatch.crema: -------------------------------------------------------------------------------- 1 | def int foo() 2 | { 3 | return 32.3 4 | } 5 | -------------------------------------------------------------------------------- /tests/fail/list_invalid_index.crema: -------------------------------------------------------------------------------- 1 | int foo[] 2 | foo[] = 3 3 | foo[3.5] = 1 4 | -------------------------------------------------------------------------------- /tests/success/if_stmt.crema: -------------------------------------------------------------------------------- 1 | int x=0 2 | int y=1 3 | int z 4 | if (x==0) {z=2} 5 | -------------------------------------------------------------------------------- /tests/success/struct_decl.crema: -------------------------------------------------------------------------------- 1 | struct foo { 2 | int a, 3 | double b 4 | } 5 | -------------------------------------------------------------------------------- /tests/success/list_binary_op.crema: -------------------------------------------------------------------------------- 1 | int foo[] = [1, 2, 3] 2 | int bar = foo[1] + 32 3 | -------------------------------------------------------------------------------- /tests/fail/struct_duplicate_members.crema: -------------------------------------------------------------------------------- 1 | struct foo { 2 | int a 3 | double a 4 | } 5 | -------------------------------------------------------------------------------- /tests/fail/duplicate_struct_member.crema: -------------------------------------------------------------------------------- 1 | struct foo { 2 | int a, 3 | double a 4 | } 5 | 6 | -------------------------------------------------------------------------------- /tests/success/if_else_stmt.crema: -------------------------------------------------------------------------------- 1 | int x=0 2 | int y=1 3 | int z 4 | if (x==0) {z=2} else {z=3} 5 | -------------------------------------------------------------------------------- /tests/fail/loop_no_list.crema: -------------------------------------------------------------------------------- 1 | int sum = 0 2 | foreach(list as elem) { 3 | sum = sum + elem 4 | } 5 | -------------------------------------------------------------------------------- /contributors.txt: -------------------------------------------------------------------------------- 1 | Jacob Torrey 2 | C. Trent Brunson 3 | Mark Bridgman 4 | Jared Wright 5 | Doug Dotson 6 | -------------------------------------------------------------------------------- /tests/success/else_if_stmt.crema: -------------------------------------------------------------------------------- 1 | int x=0 2 | int y=1 3 | int z 4 | if (x==1) {z=3} else if (x==0) {z=2} 5 | -------------------------------------------------------------------------------- /tests/fail/loop_non_list.crema: -------------------------------------------------------------------------------- 1 | int a = 1 2 | int sum = 0 3 | foreach (a as b) { 4 | sum = sum + b 5 | } 6 | -------------------------------------------------------------------------------- /tests/success/func_call.crema: -------------------------------------------------------------------------------- 1 | def int foo(int a) { 2 | return a + 32 3 | } 4 | 5 | int bar = foo(123) 6 | -------------------------------------------------------------------------------- /tests/fail/func_arg_type_mismatch.crema: -------------------------------------------------------------------------------- 1 | def int foo(int a) { 2 | return 32 3 | } 4 | 5 | int bar = foo(1.0) 6 | -------------------------------------------------------------------------------- /tests/fail/func_arg_num_mismatch.crema: -------------------------------------------------------------------------------- 1 | def int foo(int a, int b) { 2 | return 32 3 | } 4 | 5 | int bar = foo(1) 6 | -------------------------------------------------------------------------------- /tests/fail/func_corecursion.crema: -------------------------------------------------------------------------------- 1 | def int foo() { 2 | return bar() 3 | } 4 | 5 | def int bar() { 6 | return foo() 7 | } -------------------------------------------------------------------------------- /tests/success/loop_add.crema: -------------------------------------------------------------------------------- 1 | int sum = 0 2 | int l[] = [1, 2, 3, 4, 5] 3 | foreach(l as elem) { 4 | sum = sum + elem 5 | } 6 | -------------------------------------------------------------------------------- /tests/success/func_returning_array.crema: -------------------------------------------------------------------------------- 1 | def int[] func(int a[]) 2 | { 3 | int result[] = [1,2,3] 4 | return result 5 | } 6 | 7 | -------------------------------------------------------------------------------- /tests/fail/func_nested.crema: -------------------------------------------------------------------------------- 1 | def void foo() 2 | { 3 | def void bar() 4 | { 5 | int a = 2 6 | } 7 | 8 | int a = 2 9 | } 10 | -------------------------------------------------------------------------------- /tests/success/parens_in_expressions.crema: -------------------------------------------------------------------------------- 1 | int a = 5 2 | int b = 6 3 | int c 4 | 5 | c = (a * b) + 3 6 | c = a * (b + 3) 7 | c = (a * (b + 3)) / 7 8 | -------------------------------------------------------------------------------- /tests/fail/indirect_recursion.crema: -------------------------------------------------------------------------------- 1 | def void func1(int n) 2 | { 3 | func2(n) 4 | } 5 | 6 | def void func2(int n) 7 | { 8 | func1(n) 9 | } 10 | -------------------------------------------------------------------------------- /tests/success/struct_member.crema: -------------------------------------------------------------------------------- 1 | struct bar { 2 | int a, 3 | int b 4 | } 5 | 6 | struct bar foo 7 | 8 | foo.a = 34 9 | int a = foo.a - 2 10 | -------------------------------------------------------------------------------- /tests/fail/recursion.crema: -------------------------------------------------------------------------------- 1 | def int fact(int n) 2 | { 3 | if (n == 0) 4 | { 5 | return 1 6 | } 7 | else 8 | { 9 | return n * fact(n - 1) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/success/func_passing_array.crema: -------------------------------------------------------------------------------- 1 | def void func(int a[]) 2 | { 3 | int b[] = a 4 | } 5 | 6 | def void func2() 7 | { 8 | int arg[] = [1,2,3] 9 | func(arg) 10 | } 11 | -------------------------------------------------------------------------------- /tests/success/nested_loops.crema: -------------------------------------------------------------------------------- 1 | int ints[] = [1,2,3] 2 | foreach (ints as i) 3 | { 4 | int a = i 5 | 6 | foreach (ints as j) 7 | { 8 | int a = j 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/success/int_operations.crema: -------------------------------------------------------------------------------- 1 | int a = 2 2 | int b = 5 3 | int result 4 | result = a + b 5 | result = a - b 6 | result = a * b 7 | result = a / b 8 | result = a % b 9 | result = a | b 10 | result = a & b 11 | result = a ^ b 12 | -------------------------------------------------------------------------------- /tests/success/loop_in_if.crema: -------------------------------------------------------------------------------- 1 | int ints[] = [1,2,3] 2 | 3 | if (1 != 2) 4 | { 5 | foreach (ints as i) 6 | { 7 | int a = i 8 | } 9 | } 10 | else 11 | { 12 | foreach (ints as i) 13 | { 14 | int a = i 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/success/double_operations.crema: -------------------------------------------------------------------------------- 1 | double a = 2 2 | double b = 5 3 | double result 4 | result = a + b 5 | result = a - b 6 | result = a * b 7 | result = a / b 8 | result = a % b 9 | result = a | b 10 | result = a & b 11 | result = a ^ b 12 | -------------------------------------------------------------------------------- /tests/success/loop_in_if_else.crema: -------------------------------------------------------------------------------- 1 | int ints[] = [1,2,3] 2 | 3 | if (1 != 2) 4 | { 5 | foreach (ints as i) 6 | { 7 | int a = i 8 | } 9 | } 10 | else 11 | { 12 | foreach (ints as i) 13 | { 14 | int a = i 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/success/if_in_nested_loops.crema: -------------------------------------------------------------------------------- 1 | int ints[] = [1,2,3] 2 | foreach (ints as i) 3 | { 4 | int a = i 5 | 6 | foreach (ints as j) 7 | { 8 | int b = j 9 | if (a == b) 10 | { 11 | int c = 0 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/mainpage.dox: -------------------------------------------------------------------------------- 1 | /** 2 | \mainpage 3 | Crema is specifically designed as a sub-Turing complete programming language. These Doxygen pages are 4 | written for developers augmenting or working on the Crema compiler itself as opposed to those writing programs 5 | in the Crema language. 6 | */ -------------------------------------------------------------------------------- /vagrant/provision.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add - 4 | sudo apt-get install clang-3.4 lldb-3.4 -y 5 | sudo apt-get install clang-3.4 clang-3.4-doc libclang-common-3.4-dev libclang-3.4-dev libclang1-3.4 libclang1-3.4-dbg libllvm-3.4-ocaml-dev libllvm3.4 libllvm3.4-dbg lldb-3.4 llvm-3.4 llvm-3.4-dev llvm-3.4-doc llvm-3.4-examples llvm-3.4-runtime clang-modernize-3.4 clang-format-3.4 python-clang-3.4 lldb-3.4-dev -y 6 | 7 | sudo apt-get install bison flex -y 8 | sudo apt-get install llvm-dev -y 9 | -------------------------------------------------------------------------------- /vagrant/Vagrant_README.txt: -------------------------------------------------------------------------------- 1 | This is an Ubuntu 14.04 64-bit Vagrant instance with llvm and clang installed. 2 | 3 | You will need to install: 4 | 5 | VirtualBox 6 | Vagrant 7 | 8 | To use this VM for development, copy Vagrantfile and provision.sh to the project's 9 | home folder. Your file structure should look like this: 10 | 11 | /project 12 | Vagrantfile 13 | provision.sh 14 | /crema 15 | README 16 | contributors.txt 17 | /docs 18 | /src 19 | /vagrant 20 | 21 | From the /project folder, type 'vagrant up' to start the VM. Then, type 'vagrant ssh' 22 | to go to the Ubuntu 14.04 terminal. You will be able to access the /crema folder from 23 | the VM. Just type 'cd ../../vagrant'. 24 | -------------------------------------------------------------------------------- /src/decls.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file decls.h 3 | @brief Header file for forward declarations 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | */ 7 | 8 | #ifndef CREMA_DECLS_H_ 9 | #define CREMA_DECLS_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | class CodeGenContext; 16 | class NExpression; 17 | class NStatement; 18 | class NVariableAssignment; 19 | class NVariableDeclaration; 20 | class NStructureDeclaration; 21 | class NFunctionDeclaration; 22 | class NValue; 23 | class NBlock; 24 | class NListAccess; 25 | class NStructureAccess; 26 | class NIdentifier; 27 | class SemanticContext; 28 | class Type; 29 | 30 | typedef std::vector StatementList; 31 | typedef std::vector ExpressionList; 32 | typedef std::vector VariableList; 33 | typedef std::vector FunctionList; 34 | typedef std::vector ValueList; 35 | 36 | extern SemanticContext rootCtx; 37 | extern CodeGenContext rootCodeGenCtx; 38 | 39 | #endif // CREMA_DECLS_H_ 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2015 Assured Information Security, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Crema Unit Test Framework 4 | # (C) Assured Information Security, Inc. 2014 5 | # 10/22/2014 6 | # 7 | # Runs all the tests in fail that are supposed to fail either parsing or semantic analysis 8 | # and all the tests in success which should succeed and collates that information into a single, 9 | # easy-to-read display. 10 | 11 | TOTALTESTS=0 12 | PASSEDTESTS=0 13 | 14 | if [ ! -f ../src/cremacc ] 15 | then 16 | echo "cremacc not found! Please build and try again!" 17 | exit -1 18 | fi 19 | 20 | echo "Running failure tests:" 21 | for FILE in $(ls fail/) 22 | do 23 | echo -n "Running test" $FILE "... " 24 | ((TOTALTESTS++)) 25 | CREMA=$(../src/cremacc -s < "fail/"$FILE &> /dev/null) 26 | if [[ $? -ne 0 ]] 27 | then 28 | echo "passed!" 29 | ((PASSEDTESTS++)) 30 | else 31 | echo "failed!" 32 | fi 33 | done 34 | 35 | echo "" 36 | echo "Running success tests:" 37 | for FILE in $(ls success/) 38 | do 39 | echo -n "Running test" $FILE "... " 40 | ((TOTALTESTS++)) 41 | CREMA=$(../src/cremacc -s < "success/"$FILE &> /dev/null) 42 | if [[ $? -eq 0 ]] 43 | then 44 | echo "passed!" 45 | ((PASSEDTESTS++)) 46 | else 47 | echo "failed!" 48 | fi 49 | done 50 | 51 | echo "" 52 | echo "Passed " $PASSEDTESTS "/" $TOTALTESTS "!" 53 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC := g++ #clang++ 2 | 3 | DEP_FILES := parser.cpp lexer.cpp ast.cpp codegen.cpp types.cpp semantics.cpp crema.cpp 4 | OBJ_FILES := parser.o lexer.o ast.o codegen.o types.o semantics.o crema.o 5 | CPP_FLAGS := `llvm-config --cxxflags` -Wno-cast-qual -std=c++11 -g 6 | LD_FLAGS := `llvm-config --ldflags` 7 | LIBS := `llvm-config --libs core jit native interpreter` 8 | 9 | all: cremacc 10 | 11 | cremacc: parser.o lexer.o ast.o types.o crema.o codegen.o semantics.o 12 | $(CC) -std=c++11 -o cremacc $(OBJ_FILES) $(LIBS) $(LD_FLAGS) 13 | 14 | parser.o: parser.h 15 | $(CC) -c $(CPP_FLAGS) parser.cpp 16 | 17 | parser.h: parser.y ast.h 18 | bison -d -o parser.cpp --defines=parser.h parser.y 19 | 20 | lexer.o: lexer.cpp 21 | $(CC) -c $(CPP_FLAGS) lexer.cpp 22 | 23 | lexer.cpp: lexer.l ast.h 24 | flex -olexer.cpp lexer.l 25 | 26 | ast.o: ast.cpp ast.h 27 | $(CC) -c $(CPP_FLAGS) ast.cpp 28 | 29 | types.o: types.cpp types.h ast.h 30 | $(CC) -c $(CPP_FLAGS) types.cpp 31 | 32 | codegen.o: codegen.cpp parser.h codegen.h ast.h types.h 33 | $(CC) -c $(CPP_FLAGS) codegen.cpp 34 | 35 | crema.o: crema.cpp ast.h 36 | $(CC) -c $(CPP_FLAGS) crema.cpp 37 | 38 | semantics.o: semantics.cpp parser.h semantics.h ast.h 39 | $(CC) -std=c++11 -c $(CPP_FLAGS) semantics.cpp 40 | 41 | graph: 42 | bison -d -o parser.cpp --defines=parser.h -g parser.y 43 | dot -Tpng parser.dot > parser.png 44 | 45 | doc: clean 46 | cd ../docs/doxygen && doxygen 47 | 48 | clean: 49 | -rm *~ *.o cremacc parser.cpp parser.h lexer.cpp 50 | -------------------------------------------------------------------------------- /src/semantics.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file semantics.h 3 | @brief Header file for classes and routines related to semantic checking 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | Contains the definitions of classes and functionality needed to perform 8 | semantic checking on a Crema program's AST 9 | */ 10 | 11 | #ifndef CREMA_SEMANTICS_H_ 12 | #define CREMA_SEMANTICS_H_ 13 | 14 | #include "decls.h" 15 | #include "types.h" 16 | 17 | /** 18 | * Stores the contextual information required to perform semantic analysis on a Crema program */ 19 | class SemanticContext { 20 | public: 21 | int currScope; /**< Index to the current scope used for variable search */ 22 | bool inList, inFunc; 23 | std::vector vars; /**< Stack of scopes containing declared variables */ 24 | std::vector currType; /**< List of return types for the stack of scopes */ 25 | std::vector funcReturns; /**< List of bools to see if the function has a matching return statement */ 26 | std::vector structs; /**< List of defined structures */ 27 | FunctionList funcs; /**< List of defined functions */ 28 | 29 | SemanticContext(); /**< Default constructor, creates the root (empty) scope */ 30 | void newScope(Type & type); 31 | void delScope(); 32 | NStructureDeclaration * searchStructs(NIdentifier & ident); 33 | NVariableDeclaration * searchVars(NIdentifier & ident); 34 | NFunctionDeclaration * searchFuncs(NIdentifier & ident); 35 | bool registerVar(NVariableDeclaration * var); 36 | bool registerFunc(NFunctionDeclaration * func); 37 | bool registerStruct(NStructureDeclaration * s); 38 | }; 39 | 40 | #endif // CREMA_SEMANTICS_H_ 41 | -------------------------------------------------------------------------------- /src/codegen.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file codegen.h 3 | @brief Header file for code generation routines 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | A header containing definitions and declarations for functionality 8 | related to generating LLVM IR bytecode for a Crema program 9 | */ 10 | 11 | #ifndef CREMA_CODEGEN_H_ 12 | #define CREMA_CODEGEN_H_ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | class NBlock; 31 | class NVariableDeclaration; 32 | 33 | class CodeGenContext 34 | { 35 | public: 36 | llvm::Module * rootModule; 37 | llvm::IRBuilder<> * Builder; 38 | llvm::Function *mainFunction; 39 | std::stack blocks, listblocks; 40 | std::vector > > variables; 41 | // std::vector > > functions; 42 | 43 | CodeGenContext(); 44 | ~CodeGenContext() { delete Builder; } 45 | void codeGen(NBlock * rootBlock); 46 | llvm::Value * findVariable(std::string ident); 47 | NVariableDeclaration * findVariableDeclaration(std::string ident); 48 | void addVariable(NVariableDeclaration * var, llvm::Value * value); 49 | llvm::GenericValue runProgram(); 50 | void dump() { rootModule->dump(); } 51 | }; 52 | 53 | llvm::Value * CodeGenError(const char *); 54 | 55 | 56 | #endif // CREMA_CODEGEN_H_ 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Crema 2 | (C) 2014-2015 Assured Information Security, Inc. 3 | 4 | ## Introduction 5 | Crema is a LLVM front-end that aims to specifically execute in sub-Turing Complete space. 6 | Designed to be simple to learn, and practical for the majority of programming tasks needed, 7 | Crema can restrict the computational complexity of the program to the minimum needed to improve 8 | security. 9 | 10 | ## Technical Details 11 | Crema is developed in C++ in order to natively utilize the LLVM tool-chain and integrate with 12 | flex/bison to simplify parser generation. 13 | 14 | ## Getting started 15 | Start off by cloning the latest version of Crema from this git repository 16 | 17 | ### Building the Crema Tool 18 | Before building the Crema tool, make sure that you have the following dependencies installed on your environment: 19 | 20 | 1. g++ 21 | 2. bison: http://www.gnu.org/software/bison/ 22 | 3. llvm-dev 23 | 4. flex: http://flex.sourceforge.net/ 24 | 25 | Build the Crema tool by running ```make``` in the src directory. This will create the "cremacc" program. You can clean the src directory by running ```make clean``` 26 | 27 | ### Compiling your Crema programs with cremacc 28 | To compile a Crema program into LLVM IR for JIT execution, simply use the "cremacc" program. 29 | 30 | Crema files use the extention ".crema". To build a crema file into an executable named "program.exe", use the command: 31 | ``` 32 | ./cremacc -f -o program 33 | ``` 34 | 35 | To print LLVM assembly to a file, use the -S option: 36 | ``` ./cremacc -f -S .ll``` 37 | 38 | For help with all of the other command line options available for cremacc, simply run: 39 | ```./cremacc -h``` 40 | 41 | ## Learning Crema 42 | Use this repository's Wiki page to get started with code examples, reference information, and other general topics about the Crema language. 43 | 44 | Distribution Statement "A" (Approved for Public Release, Distribution Unlimited) 45 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file types.h 3 | @brief Header file for type classes used to perform type-checking during the semantic analysis checks 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | The Type family of classes are used to store type information about various expressions and 8 | AST nodes to assist in the analysis of typing during semantic analysis. 9 | */ 10 | 11 | #ifndef CREMA_TYPE_H_ 12 | #define CREMA_TYPE_H_ 13 | 14 | #include 15 | #include 16 | 17 | class NIdentifier; 18 | 19 | /** 20 | * Enumeration of types available to Crema programs */ 21 | enum TypeCodes { 22 | INT, 23 | DOUBLE, 24 | STRING, 25 | CHAR, 26 | VOID, 27 | BOOL, 28 | UINT, 29 | STRUCT, 30 | INVALID 31 | }; 32 | 33 | /** 34 | * Root type class */ 35 | class Type { 36 | protected: 37 | void setType(int type); 38 | public: 39 | bool isList; /**< Bool value if the type is a list-type */ 40 | bool isStruct; /**< Bool value if the type is a struct type */ 41 | TypeCodes typecode; /**< typecode for the Type */ 42 | virtual ~Type() { } 43 | Type() : typecode(INVALID) { } 44 | Type(int type) { isList = false; setType(type); isStruct = false; } 45 | Type(int type, bool l) { setType(type); isList = l; isStruct = false; } 46 | Type(Type & t, bool l) { typecode = t.typecode; isList = l; isStruct = false; } 47 | bool getIsList() { return isList; } 48 | size_t getSize(); 49 | llvm::Type * toLlvmType(); 50 | static Type & getLargerType(Type & t1, Type & t2); 51 | virtual std::ostream & print(std::ostream & os) const; 52 | friend std::ostream & operator<<(std::ostream & os, Type & type); 53 | friend bool operator==(Type & t1, Type & t2); 54 | friend bool operator!=(Type & t1, Type & t2); 55 | friend bool operator>(Type & t1, Type & t2); 56 | friend bool operator>=(Type & t1, Type & t2); 57 | friend bool operator<(Type & t1, Type & t2); 58 | friend bool operator<=(Type & t1, Type & t2); 59 | }; 60 | 61 | class StructType : public Type 62 | { 63 | public: 64 | NIdentifier & ident; 65 | StructType(NIdentifier & ident) : ident(ident) { setType(STRUCT); isStruct = true; } 66 | std::ostream & print(std::ostream & os) const; 67 | }; 68 | 69 | #endif // CREMA_TYPE_H_ 70 | -------------------------------------------------------------------------------- /src/lexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include "ast.h" 4 | #include "parser.h" 5 | #define SAVE_VAL yylval.string = new std::string(yytext, yyleng) 6 | #define TOK(t) (yylval.token = (t)) 7 | 8 | extern "C" int yywrap() { } 9 | %} 10 | 11 | %option debug yylineno nodefault 12 | 13 | %x incl 14 | 15 | %% 16 | 17 | "include" BEGIN(incl); 18 | 19 | [ \t]* 20 | [^ \t\n]+ { 21 | yyin = fopen( yytext, "r" ); 22 | 23 | if ( ! yyin ) 24 | { 25 | printf("WARNING: Unable to include file %s\n", yytext); 26 | yyterminate(); 27 | } 28 | yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE )); 29 | 30 | BEGIN(INITIAL); 31 | } 32 | 33 | <> { 34 | yypop_buffer_state(); 35 | 36 | if ( !YY_CURRENT_BUFFER ) 37 | { 38 | yyterminate(); 39 | } 40 | } 41 | 42 | [ \t] ; 43 | \n ; // TODO: Keep track of line count to provide more helpful error reporting 44 | "extern" return TOK(TEXTERN); 45 | "return" return TOK(TRETURN); 46 | "break" return TOK(TBREAK); 47 | "sdef" return TOK(TSDEF); 48 | "def" return TOK(TDEF); 49 | "struct" return TOK(TTSTRUCT); 50 | "if" return TOK(TIF); 51 | "else" return TOK(TELSE); 52 | "foreach" return TOK(TFOREACH); 53 | "as" return TOK(TAS); 54 | "eq" return TOK(TCEQ); 55 | "neq" return TOK(TCNEQ); 56 | "le" return TOK(TCLE); 57 | "ge" return TOK(TCGE); 58 | "gt" return TOK(TCGT); 59 | "lt" return TOK(TCLT); 60 | "true" return TOK(TTRUE); 61 | "false" return TOK(TFALSE); 62 | "bool" return TOK(TTBOOL); 63 | "char" return TOK(TTCHAR); 64 | "void" return TOK(TTVOID); 65 | "int" return TOK(TTINT); 66 | "uint" return TOK(TTUINT); 67 | "string" return TOK(TTSTR); 68 | "double" return TOK(TTDOUBLE); 69 | [a-zA-Z_][a-zA-Z_0-9]* SAVE_VAL; return TIDENTIFIER; 70 | [0-9]+\.[0-9]+ SAVE_VAL; return TDOUBLE; 71 | [0-9]+ SAVE_VAL; return TINT; 72 | \"[^\"\n]*\" SAVE_VAL; return TSTRING; 73 | \'.\' SAVE_VAL; return TCHAR; 74 | \'\\n\' SAVE_VAL; return TCHAR; 75 | \'\\t\' SAVE_VAL; return TCHAR; 76 | "=" return TOK(TEQUAL); 77 | "==" return TOK(TCEQ); 78 | "!=" return TOK(TCNEQ); 79 | "<" return TOK(TCLT); 80 | "<=" return TOK(TCLE); 81 | ">" return TOK(TCGT); 82 | ">=" return TOK(TCGE); 83 | "(" return TOK(TLPAREN); 84 | ")" return TOK(TRPAREN); 85 | "[" return TOK(TLBRAC); 86 | "]" return TOK(TRBRAC); 87 | "%" return TOK(TMOD); 88 | "*" return TOK(TMUL); 89 | "/" return TOK(TDIV); 90 | "+" return TOK(TADD); 91 | "-" return TOK(TSUB); 92 | "&" return TOK(TBAND); 93 | "^" return TOK(TBXOR); 94 | "|" return TOK(TBOR); 95 | "||" return TOK(TLOR); 96 | "&&" return TOK(TLAND); 97 | "!" return TOK(TLNOT); 98 | "{" return TOK(TLBRACKET); 99 | "}" return TOK(TRBRACKET); 100 | "," return TOK(TCOMMA); 101 | "\." return TOK(TPERIOD); 102 | \#.* ; // Comment, ignore 103 | . printf("Unknown token on line %d\n", yylineno); yyterminate(); 104 | 105 | %% 106 | -------------------------------------------------------------------------------- /src/stdlib/stdlib.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file stdlib.h 3 | @brief Header for standard C routines that support Crema programs 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | A header defining functions and other structures needed to support 8 | in the execution of Crema programs 9 | */ 10 | #ifndef CREMA_STDLIB_H_ 11 | #define CREMA_STDLIB_H_ 12 | 13 | #include 14 | #include 15 | 16 | struct list_s { 17 | unsigned int cap; 18 | int64_t len; 19 | size_t elem_sz; 20 | void * arr; 21 | }; 22 | 23 | typedef struct list_s list_t; 24 | typedef list_t string_t; 25 | 26 | #define DEFAULT_RESIZE_AMT 5 27 | 28 | list_t * list_create(int64_t es); 29 | void list_free(list_t * list); 30 | void list_insert(list_t * list, unsigned int idx, void * elem); 31 | void * list_retrieve(list_t * list, unsigned int idx); 32 | void list_append(list_t * list, void * elem); 33 | void list_concat(list_t * list1, list_t * list2); 34 | void list_delete(list_t * list, unsigned int idx); 35 | int64_t list_length(list_t * list); 36 | 37 | string_t * str_create(); 38 | void str_free(string_t * str); 39 | void str_insert(string_t * str, unsigned int idx, char elem); 40 | char str_retrieve(string_t * str, unsigned int idx); 41 | void str_append(string_t * str, char elem); 42 | void str_concat(string_t * str1, string_t * str2); 43 | void str_print(string_t * str); 44 | void str_println(string_t * str); 45 | void str_delete(string_t * str, unsigned int idx); 46 | string_t * str_substr(string_t * str, unsigned int start, unsigned int len); 47 | 48 | list_t * int_list_create(); 49 | void int_list_insert(list_t * list, int64_t idx, int64_t val); 50 | int64_t int_list_retrieve(list_t * list, int64_t idx); 51 | void int_list_append(list_t * list, int64_t elem); 52 | 53 | list_t * double_list_create(); 54 | void double_list_insert(list_t * list, unsigned int idx, double val); 55 | double double_list_retrieve(list_t * list, unsigned int idx); 56 | void double_list_append(list_t * list, double elem); 57 | void double_print(double val); 58 | void double_println(double val); 59 | 60 | list_t * crema_seq(int64_t start, int64_t end); 61 | 62 | void int_print(int64_t val); 63 | void int_println(int64_t val); 64 | void make_symbolic(list_t * list); 65 | 66 | void save_args(int64_t argc, char ** argv); 67 | int64_t prog_arg_count(); 68 | list_t * prog_argument(int64_t idx); 69 | 70 | // *********************** Type Conversion ***************************** // 71 | int64_t double_to_int(double val); 72 | double int_to_double(int64_t val); 73 | string_t * int_to_string(int64_t val); 74 | int64_t string_to_int(string_t * str); 75 | int64_t string_to_double(string_t * str); 76 | 77 | // ************************ Math Functions ***************************** // 78 | double double_floor(double val); 79 | double double_ceiling(double val); 80 | double double_round(double val); 81 | double double_truncate(double val); 82 | double double_square(double val); 83 | int64_t int_square(int64_t val); 84 | double double_pow(double base, double power); 85 | int64_t int_pow(int64_t base, int64_t power); 86 | double double_sin(double val); 87 | double double_cos(double val); 88 | double double_tan(double val); 89 | double double_sqrt(double val); 90 | double double_abs(double val); 91 | int64_t int_abs(int64_t val); 92 | 93 | #endif // CREMA_STDLIB_H_ 94 | -------------------------------------------------------------------------------- /src/crema.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file crema.cpp 3 | @brief Main cremacc file and main() function 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | A main function to read in an input. It will parse and perform semantic 8 | analysis on the input and either exit(0) if it passes both, -1 otherwise. 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "ast.h" 15 | #include "codegen.h" 16 | #include "ezOptionParser.hpp" 17 | #include "llvm/CodeGen/AsmPrinter.h" 18 | #include 19 | #include 20 | 21 | extern NBlock *rootBlock; 22 | extern int yyparse(); 23 | extern "C" FILE *yyin; 24 | 25 | void yyset_debug(int); 26 | 27 | int main(int argc, const char *argv[]) 28 | { 29 | // Handling command-line options 30 | ez::ezOptionParser opt; 31 | opt.overview = "Crema Compiler for Sub-Turing Complete Programs"; 32 | opt.syntax = "cremacc [OPTIONS]"; 33 | opt.footer = "\n(C) 2014 Assured Information Security, Inc.\n"; 34 | 35 | opt.add("", 0, 0, 0, "Prints this help", "-h"); 36 | opt.add("", 0, 0, 0, "Parse only: Will halt after parsing and pretty-printing the AST for the input program", "-p"); 37 | opt.add("", 0, 0, 0, "Semantic check only: Will halt after parsing, pretty-printing and performing semantic checks on the AST for the input program", "-s"); 38 | opt.add("", 0, 1, 0, "Print LLVM Assembly to file", "-S"); 39 | opt.add("", 0, 1, 0, "Set the output program name to ARG instead of 'a.out'", "-o"); 40 | opt.add("", 0, 1, 0, "Read input from file instead of stdin", "-f"); 41 | opt.add("", 0, 0, 0, "Print parser output and root block", "-v"); 42 | 43 | opt.parse(argc, argv); 44 | 45 | if (opt.isSet("-h")) 46 | { 47 | std::string usage; 48 | opt.getUsage(usage); 49 | std::cout << usage; 50 | return 0; 51 | } 52 | 53 | if (opt.isSet("-v")) { 54 | yyset_debug(1); 55 | } else { 56 | yyset_debug(0); 57 | } 58 | // Parse input 59 | if (opt.isSet("-f")) { 60 | // searches for the -f flag 61 | int i=0; 62 | while (argv[i] != std::string("-f")) 63 | ++i; 64 | 65 | // reads the file name string after the -f flag 66 | FILE *inFile = fopen(argv[i+1],"r"); 67 | if (!inFile) 68 | std::cout << "Cannot open file, " << argv[i+1] << ".\n" << "Usage: ./cremacc -f \n"; 69 | yyin = inFile; 70 | 71 | // feeds the input file into cremacc 72 | do { 73 | yyparse(); 74 | } while (!feof(yyin)); 75 | } 76 | else 77 | yyparse(); // no -f flag will produce the commandline cremacc 78 | 79 | if (opt.isSet("-p")) 80 | { 81 | return 0; 82 | } 83 | 84 | // Perform semantic checks 85 | if (rootBlock) 86 | { 87 | if (opt.isSet("-v")) { 88 | std::cout << *rootBlock << std::endl; 89 | } 90 | if (rootBlock->semanticAnalysis(&rootCtx)) 91 | { 92 | std::cout << "Passed semantic analysis!" << std::endl; 93 | } 94 | else 95 | { 96 | std::cout << "Failed semantic analysis!" << std::endl; 97 | return -1; 98 | } 99 | } 100 | if (opt.isSet("-s")) 101 | { 102 | return 0; 103 | } 104 | 105 | // Code Generation 106 | std::cout << "Generating LLVM IR bytecode" << std::endl; 107 | rootCodeGenCtx.codeGen(rootBlock); 108 | 109 | if (opt.isSet("-S")) 110 | { 111 | // searches for the -S flag 112 | int i=0; 113 | while (argv[i] != std::string("-S")) 114 | ++i; 115 | 116 | // writes output LLVM assembly to argument after -S flag 117 | FILE *outFile; 118 | outFile = freopen(argv[i+1],"w",stderr); 119 | rootCodeGenCtx.dump(); 120 | fclose(outFile); 121 | } 122 | 123 | char tmpname[12] = "crematmp.ll"; 124 | FILE *outFile; 125 | outFile = freopen(tmpname, "wb", stderr); 126 | rootCodeGenCtx.dump(); 127 | fclose(outFile); 128 | 129 | std::ostringstream oss; 130 | std::cout << "Linking with stdlib.c using clang..." << std::endl; 131 | std::string outputname = ""; 132 | if (opt.isSet("-o")) 133 | { 134 | // searches for the -S flag 135 | int i=0; 136 | while (argv[i] != std::string("-o")) 137 | ++i; 138 | outputname = "-o " + std::string(argv[i + 1]); 139 | } 140 | oss << "clang " << outputname << " " << tmpname << " stdlib/stdlib.c -lm"; 141 | std::string cmd = oss.str(); 142 | // runs the command: clang <.ll filename> 143 | if(std::system(cmd.c_str())) 144 | { 145 | std::cout << "ERROR: Unable to build program with CLANG!" << std::endl; 146 | return -1; 147 | } 148 | unlink(tmpname); 149 | 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /vagrant/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure("2") do |config| 8 | # All Vagrant configuration is done here. The most common configuration 9 | # options are documented and commented below. For a complete reference, 10 | # please see the online documentation at vagrantup.com. 11 | config.vm.provision :shell, path: "provision.sh" 12 | config.vm.network :forwarded_port, host: 4567, guest: 80 13 | config.vm.provider "virtualbox" do |v| 14 | v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] 15 | v.customize ["modifyvm", :id, "--natdnsproxy1", "on"] 16 | end 17 | 18 | # Every Vagrant virtual environment requires a box to build off of. 19 | config.vm.box = "ubuntu/trusty64" 20 | # Disable automatic box update checking. If you disable this, then 21 | # boxes will only be checked for updates when the user runs 22 | # `vagrant box outdated`. This is not recommended. 23 | # config.vm.box_check_update = false 24 | 25 | # Create a forwarded port mapping which allows access to a specific port 26 | # within the machine from a port on the host machine. In the example below, 27 | # accessing "localhost:8080" will access port 80 on the guest machine. 28 | # config.vm.network "forwarded_port", guest: 80, host: 8080 29 | 30 | # Create a private network, which allows host-only access to the machine 31 | # using a specific IP. 32 | # config.vm.network "private_network", ip: "192.168.33.10" 33 | 34 | # Create a public network, which generally matched to bridged network. 35 | # Bridged networks make the machine appear as another physical device on 36 | # your network. 37 | # config.vm.network "public_network" 38 | 39 | # If true, then any SSH connections made will enable agent forwarding. 40 | # Default value: false 41 | # config.ssh.forward_agent = true 42 | 43 | # Share an additional folder to the guest VM. The first argument is 44 | # the path on the host to the actual folder. The second argument is 45 | # the path on the guest to mount the folder. And the optional third 46 | # argument is a set of non-required options. 47 | # config.vm.synced_folder "../data", "/vagrant_data" 48 | 49 | # Provider-specific configuration so you can fine-tune various 50 | # backing providers for Vagrant. These expose provider-specific options. 51 | # Example for VirtualBox: 52 | # 53 | # config.vm.provider "virtualbox" do |vb| 54 | # # Don't boot with headless mode 55 | # vb.gui = true 56 | # 57 | # # Use VBoxManage to customize the VM. For example to change memory: 58 | # vb.customize ["modifyvm", :id, "--memory", "1024"] 59 | # end 60 | # 61 | # View the documentation for the provider you're using for more 62 | # information on available options. 63 | 64 | # Enable provisioning with CFEngine. CFEngine Community packages are 65 | # automatically installed. For example, configure the host as a 66 | # policy server and optionally a policy file to run: 67 | # 68 | # config.vm.provision "cfengine" do |cf| 69 | # cf.am_policy_hub = true 70 | # # cf.run_file = "motd.cf" 71 | # end 72 | # 73 | # You can also configure and bootstrap a client to an existing 74 | # policy server: 75 | # 76 | # config.vm.provision "cfengine" do |cf| 77 | # cf.policy_server_address = "10.0.2.15" 78 | # end 79 | 80 | # Enable provisioning with Puppet stand alone. Puppet manifests 81 | # are contained in a directory path relative to this Vagrantfile. 82 | # You will need to create the manifests directory and a manifest in 83 | # the file default.pp in the manifests_path directory. 84 | # 85 | # config.vm.provision "puppet" do |puppet| 86 | # puppet.manifests_path = "manifests" 87 | # puppet.manifest_file = "default.pp" 88 | # end 89 | 90 | # Enable provisioning with chef solo, specifying a cookbooks path, roles 91 | # path, and data_bags path (all relative to this Vagrantfile), and adding 92 | # some recipes and/or roles. 93 | # 94 | # config.vm.provision "chef_solo" do |chef| 95 | # chef.cookbooks_path = "../my-recipes/cookbooks" 96 | # chef.roles_path = "../my-recipes/roles" 97 | # chef.data_bags_path = "../my-recipes/data_bags" 98 | # chef.add_recipe "mysql" 99 | # chef.add_role "web" 100 | # 101 | # # You may also specify custom JSON attributes: 102 | # chef.json = { mysql_password: "foo" } 103 | # end 104 | 105 | # Enable provisioning with chef server, specifying the chef server URL, 106 | # and the path to the validation key (relative to this Vagrantfile). 107 | # 108 | # The Opscode Platform uses HTTPS. Substitute your organization for 109 | # ORGNAME in the URL and validation key. 110 | # 111 | # If you have your own Chef Server, use the appropriate URL, which may be 112 | # HTTP instead of HTTPS depending on your configuration. Also change the 113 | # validation key to validation.pem. 114 | # 115 | # config.vm.provision "chef_client" do |chef| 116 | # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME" 117 | # chef.validation_key_path = "ORGNAME-validator.pem" 118 | # end 119 | # 120 | # If you're using the Opscode platform, your validator client is 121 | # ORGNAME-validator, replacing ORGNAME with your organization name. 122 | # 123 | # If you have your own Chef Server, the default validation client name is 124 | # chef-validator, unless you changed the configuration. 125 | # 126 | # chef.validation_client_name = "ORGNAME-validator" 127 | end 128 | -------------------------------------------------------------------------------- /src/types.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file types.cpp 3 | @brief Contains type class implementation 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | This file contains the function implementations for all type-related 8 | semantic checks. 9 | */ 10 | 11 | #include "types.h" 12 | #include "ast.h" 13 | #include "parser.h" 14 | 15 | /** 16 | Equality operator for Types 17 | 18 | @param t1 First Type to compare against 19 | @param t2 Second Type to compare against 20 | @return true if the types are the same in type and dimension, false otherwise 21 | */ 22 | bool operator==(Type & t1, Type & t2) 23 | { 24 | return (t1.typecode == t2.typecode) && (t1.isList == t2.isList); 25 | } 26 | 27 | /** 28 | Non-equality operator for Types 29 | 30 | @param t1 First Type to compare against 31 | @param t2 Second Type to compare against 32 | @return false if the types are the same in type and dimension, true otherwise 33 | */ 34 | bool operator!=(Type & t1, Type & t2) 35 | { 36 | return !(t1 == t2); 37 | } 38 | 39 | /** 40 | Greater than operator for Types 41 | 42 | One Type is considered "larger" than another if it can upcast. 43 | E.g. DOUBLE > INT as integers can be upcast to doubles, but not vice versa 44 | 45 | @param t1 First Type to compare 46 | @param t2 Second Type to compare 47 | @return true is t1 is "larger" than t2, false otherwise 48 | */ 49 | bool operator>(Type & t1, Type & t2) 50 | { 51 | // List types go with list types, singtons with singletons 52 | if (t1.isList != t2.isList) 53 | { 54 | return false; 55 | } 56 | 57 | // Double > Int 58 | if (t1.typecode == DOUBLE && (t2.typecode == INT || t2.typecode == UINT)) 59 | { 60 | return true; 61 | } 62 | 63 | // Int > Char 64 | if (t1.typecode == INT && t2.typecode == CHAR) 65 | { 66 | return true; 67 | } 68 | 69 | // Double/Int > Bool (true = 1, false = 0) 70 | if ((t1.typecode == INT || t2.typecode == UINT || t2.typecode == DOUBLE) && t2.typecode == BOOL) 71 | { 72 | return true; 73 | } 74 | 75 | // Bool > Double/Int 76 | if (t1.typecode == BOOL && (t2.typecode == DOUBLE || t2.typecode == INT || t2.typecode == UINT)) 77 | { 78 | return true; 79 | } 80 | 81 | // String > Double/Int 82 | if (t1.typecode == STRING && (t2.typecode == UINT || t2.typecode == INT || t2.typecode == DOUBLE)) 83 | { 84 | return true; 85 | } 86 | 87 | return false; 88 | } 89 | 90 | /** 91 | Less than operator for Types 92 | 93 | One Type is considered "larger" than another if it can upcast. 94 | E.g. DOUBLE >= INT as integers can be upcast to doubles, but not vice versa 95 | 96 | @param t1 First Type to compare 97 | @param t2 Second Type to compare 98 | @return true is t1 is "smaller" than to t2, false otherwise 99 | */ 100 | bool operator<(Type & t1, Type & t2) 101 | { 102 | return !(t1 >= t2); 103 | } 104 | 105 | /** 106 | Greater than or equal operator for Types 107 | 108 | One Type is considered "larger" than another if it can upcast. 109 | E.g. DOUBLE >= INT as integers can be upcast to doubles, but not vice versa 110 | 111 | @param t1 First Type to compare 112 | @param t2 Second Type to compare 113 | @return true is t1 is "larger" than or equal to t2, false otherwise 114 | */ 115 | bool operator>=(Type & t1, Type & t2) 116 | { 117 | return (t1 == t2) || (t1 > t2); 118 | } 119 | 120 | /** 121 | Less than or equal operator for Types 122 | 123 | One Type is considered "larger" than another if it can upcast. 124 | E.g. DOUBLE >= INT as integers can be upcast to doubles, but not vice versa 125 | 126 | @param t1 First Type to compare 127 | @param t2 Second Type to compare 128 | @return true is t1 is "smaller" than or equal to t2, false otherwise 129 | */ 130 | bool operator<=(Type & t1, Type & t2) 131 | { 132 | return (t1 == t2) || (t1 < t2); 133 | } 134 | 135 | /** 136 | Returns the larger of two passed types 137 | 138 | @param t1 First Type to be compared 139 | @param t2 Second Type to be compared 140 | 141 | @return Larger of the two Types 142 | */ 143 | Type & Type::getLargerType(Type & t1, Type & t2) 144 | { 145 | if (t1 == t2) 146 | { 147 | return t1; 148 | } 149 | if (t1 > t2) 150 | { 151 | return t1; 152 | } 153 | if (t2 > t1) 154 | { 155 | return t2; 156 | } 157 | return *(new Type()); 158 | } 159 | 160 | /** 161 | Pretty-prints a Type object 162 | 163 | @param os Stream to print to 164 | @param type Type to print 165 | @return Stream printed to 166 | */ 167 | std::ostream & operator<<(std::ostream & os, Type & type) 168 | { 169 | type.print(os); 170 | return os; 171 | } 172 | 173 | std::ostream & Type::print(std::ostream & os) const 174 | { 175 | switch(typecode) 176 | { 177 | case INT: 178 | os << "INT"; 179 | break; 180 | case CHAR: 181 | os << "CHAR"; 182 | break; 183 | case UINT: 184 | os << "UINT"; 185 | break; 186 | case DOUBLE: 187 | os << "DOUBLE"; 188 | break; 189 | case STRING: 190 | os << "STRING"; 191 | break; 192 | case BOOL: 193 | os << "BOOL"; 194 | break; 195 | case VOID: 196 | os << "VOID"; 197 | break; 198 | case STRUCT: 199 | os << "STRUCT"; 200 | break; 201 | case INVALID: 202 | os << "INVALID"; 203 | break; 204 | default: 205 | os << "UNKNOWN TYPE!"; 206 | break; 207 | } 208 | if (isList) 209 | { 210 | os << "[]"; 211 | } 212 | return os; 213 | } 214 | 215 | std::ostream & StructType::print(std::ostream & os) const 216 | { 217 | os << "STRUCT " << ident; 218 | return os; 219 | } 220 | 221 | /** 222 | Function to convert a Crema Type to an llvm::Type 223 | 224 | @return LLVM type object corresponding with the Type 225 | */ 226 | llvm::Type * Type::toLlvmType() 227 | { 228 | llvm::Type * t; 229 | if (isList) 230 | { 231 | t = llvm::PointerType::get(llvm::Type::getInt8Ty(llvm::getGlobalContext()), 0); 232 | return t; 233 | } 234 | switch(typecode) 235 | { 236 | case INT: 237 | t = llvm::Type::getInt64Ty(llvm::getGlobalContext()); 238 | break; 239 | case DOUBLE: 240 | t = llvm::Type::getDoubleTy(llvm::getGlobalContext()); 241 | break; 242 | case VOID: 243 | t = llvm::Type::getVoidTy(llvm::getGlobalContext()); 244 | break; 245 | case BOOL: 246 | t = llvm::Type::getInt1Ty(llvm::getGlobalContext()); 247 | break; 248 | case CHAR: 249 | t = llvm::Type::getInt8Ty(llvm::getGlobalContext()); 250 | break; 251 | case STRING: 252 | t = llvm::PointerType::get(llvm::Type::getInt8Ty(llvm::getGlobalContext()), 0); 253 | break; 254 | default: 255 | return NULL; 256 | break; 257 | } 258 | return t; 259 | } 260 | 261 | size_t Type::getSize() 262 | { 263 | switch(typecode) 264 | { 265 | case INT: 266 | return sizeof(int64_t); 267 | break; 268 | case DOUBLE: 269 | return sizeof(double); 270 | break; 271 | case VOID: 272 | return 0; 273 | break; 274 | case CHAR: 275 | case BOOL: 276 | return sizeof(uint8_t); 277 | break; 278 | default: 279 | return 0; 280 | break; 281 | } 282 | return 0; 283 | } 284 | 285 | /** 286 | Private function to set the typecode of a Type based on the Flex/Bison enumeration 287 | 288 | @param type Token value for type 289 | */ 290 | void Type::setType(int type) 291 | { 292 | switch(type) 293 | { 294 | case TTINT: 295 | typecode = INT; 296 | break; 297 | case TTUINT: 298 | typecode = UINT; 299 | break; 300 | case TTDOUBLE: 301 | typecode = DOUBLE; 302 | break; 303 | case TTCHAR: 304 | typecode = CHAR; 305 | break; 306 | case TTBOOL: 307 | typecode = BOOL; 308 | break; 309 | case TTSTR: 310 | typecode = CHAR; 311 | isList = true; 312 | break; 313 | case TTVOID: 314 | typecode = VOID; 315 | break; 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "decls.h" 3 | #include "ast.h" 4 | #include "semantics.h" 5 | #include 6 | #include 7 | 8 | NBlock *rootBlock; 9 | 10 | extern int yylex(); 11 | extern int yylineno; 12 | void yyerror(const char *s) { fprintf(stderr, "ERROR on line %d: %s\n", yylineno, s); exit(-1); } 13 | %} 14 | 15 | %define parse.error verbose 16 | 17 | %union { 18 | NBlock *block; 19 | Node *node; 20 | int token; 21 | NIdentifier *ident; 22 | NExpression *expression; 23 | NStatement *statement; 24 | NVariableDeclaration *var_decl; 25 | std::vector *decl_args; 26 | std::vector *call_args; 27 | std::string *string; 28 | int fn; /* which function */ 29 | } 30 | 31 | /* Terminal types */ 32 | %token TIDENTIFIER TINT TDOUBLE TCHAR TSTRING /* token strings */ 33 | %token TBREAK TRETURN TSDEF TDEF TEXTERN TIF TELSE TFOREACH TAS TTRUE TFALSE /* keywords */ 34 | %token TMUL TADD TDIV TSUB TMOD /* binary operators */ 35 | %token TCEQ TCNEQ TCLE TCGE TCLT TCGT /* comparison operators */ 36 | %token TEQUAL /* assignment operator */ 37 | %token TTBOOL TTDOUBLE TTINT TTSTR TTSTRUCT TTUINT TTVOID TTCHAR /* type tokens */ 38 | %token TLBRAC TRBRAC TLBRACKET TRBRACKET TLPAREN TRPAREN TCOMMA TPERIOD /* {} [] () , . */ 39 | %token TUMINUS /* unary operators */ 40 | %token TLAND TLOR TLNOT /* logical operators */ 41 | %token TBAND TBXOR TBOR /* bitwise operators */ 42 | 43 | /* Non-terminal types */ 44 | %type type bitwise comparison def 45 | %type identifier 46 | %type statement struct_decl var_decl break list_decl func_decl assignment return loop conditional 47 | %type expression value numeric list_access struct list var_access factor term 48 | %type block program statements 49 | %type func_call_arg_list 50 | %type func_decl_arg_list var_decls 51 | 52 | %right TEQUAL 53 | %left TBAND 54 | %left TBXOR 55 | %left TBOR 56 | %left TADD TSUB 57 | %left TMOD TMUL TDIV 58 | %nonassoc TUMINUS 59 | 60 | %start program 61 | 62 | %% 63 | 64 | program : { rootBlock = NULL; } /* Empty program */ 65 | | statements { rootBlock = $1; rootBlock->createStdlib(); } 66 | ; 67 | 68 | block : TLBRACKET statements TRBRACKET { $$ = $2; } 69 | | TLBRACKET TRBRACKET { $$ = new NBlock(); } 70 | ; 71 | 72 | statements : statement { $$ = new NBlock(); $$->statements.push_back($1); } 73 | | statements statement { $1->statements.push_back($2); } 74 | ; 75 | 76 | statement : var_decl { } 77 | | struct_decl { if(!rootCtx.registerStruct((NStructureDeclaration *) $1)) yyerror("Duplicate struct declaration!"); $$ = $1; } 78 | | func_decl { if(!rootCtx.registerFunc((NFunctionDeclaration *) $1)) yyerror("Duplicate function declaration!"); $$ = $1; } 79 | | assignment { } 80 | | identifier TLPAREN func_call_arg_list TRPAREN { $$ = new NFunctionCall(*$1, *$3); } 81 | | conditional { } 82 | | loop { } 83 | | return { } 84 | | break { } 85 | ; 86 | 87 | var_decl : type identifier { $$ = new NVariableDeclaration(*(new Type($1)), *$2); } 88 | | type identifier TEQUAL expression { $$ = new NVariableDeclaration(*(new Type($1)), *$2, $4); } 89 | | TTSTRUCT identifier identifier { $$ = new NVariableDeclaration(*(new StructType(*$2)), *$3); } 90 | | TTSTRUCT identifier identifier TEQUAL identifier { $$ = new NVariableDeclaration(*(new StructType(*$2)), *$3, $5); } 91 | | list_decl { } 92 | ; 93 | 94 | type : TTDOUBLE 95 | | TTINT 96 | | TTUINT 97 | | TTCHAR 98 | | TTSTR 99 | | TTVOID 100 | | TTBOOL 101 | ; 102 | 103 | struct_decl : TTSTRUCT identifier TLBRACKET var_decls TRBRACKET { $$ = new NStructureDeclaration(*$2, *$4); } 104 | ; 105 | 106 | var_decls : { $$ = new VariableList(); } 107 | | var_decl { $$ = new VariableList(); $$->push_back($1); } 108 | | var_decls TCOMMA var_decl { $$->push_back($3); } 109 | ; 110 | 111 | func_decl : def type identifier TLPAREN func_decl_arg_list TRPAREN block { $$ = new NFunctionDeclaration(*(new Type($2)), *$3, *$5, $7); } 112 | | def type TLBRAC TRBRAC identifier TLPAREN func_decl_arg_list TRPAREN block { $$ = new NFunctionDeclaration(*(new Type($2, true)), *$5, *$7, $9); } 113 | | TEXTERN def type identifier TLPAREN func_decl_arg_list TRPAREN { $$ = new NFunctionDeclaration(*(new Type($3)), *$4, *$6, NULL); } 114 | | TEXTERN def type TLBRAC TRBRAC identifier TLPAREN func_decl_arg_list TRPAREN { $$ = new NFunctionDeclaration(*(new Type($3, true)), *$6, *$8, NULL); } 115 | ; 116 | 117 | def : TDEF 118 | | TSDEF 119 | ; 120 | 121 | func_decl_arg_list : /* Empty */ { $$ = new VariableList(); } 122 | | var_decl { $$ = new VariableList(); $$->push_back($1); } 123 | | func_decl_arg_list TCOMMA var_decl { $$->push_back($3); } 124 | ; 125 | 126 | assignment : identifier TEQUAL expression { $$ = new NAssignmentStatement(*$1, *$3); } 127 | | list_access TEQUAL expression { $$ = new NListAssignmentStatement(((NListAccess *) $1)->ident, *((NListAccess *) $1), *$3); } 128 | | struct TEQUAL expression { $$ = new NStructureAssignmentStatement(((NStructureAccess *) $1)->ident, *((NStructureAccess *) $1), *$3); } 129 | ; 130 | 131 | list_decl : type identifier TLBRAC TRBRAC { Type *t = new Type($1, true); $$ = new NVariableDeclaration(*t, *$2); } 132 | | type identifier TLBRAC TRBRAC TEQUAL expression { Type *t = new Type($1, true); $$ = new NVariableDeclaration(*t, *$2, $6); } 133 | ; 134 | 135 | conditional : TIF TLPAREN expression TRPAREN block TELSE conditional { $$ = new NIfStatement(*$3, *$5, $7); } /* else if */ 136 | | TIF TLPAREN expression TRPAREN block TELSE block { $$ = new NIfStatement(*$3, *$5, $7); } /* else */ 137 | | TIF TLPAREN expression TRPAREN block { $$ = new NIfStatement(*$3, *$5); } /* vanilla if */ 138 | ; 139 | 140 | loop : TFOREACH TLPAREN identifier TAS identifier TRPAREN block { $$ = new NLoopStatement(*$3, *$5, *$7); } 141 | ; 142 | 143 | return : TRETURN expression { $$ = new NReturn(*$2); } 144 | ; 145 | 146 | break : TBREAK { $$ = new NBreak(); } 147 | ; 148 | 149 | expression : expression bitwise term { $$ = new NBinaryOperator(*$1, $2, *$3); } 150 | | expression TADD term { $$ = new NBinaryOperator(*$1, $2, *$3); } 151 | | expression TSUB term { $$ = new NBinaryOperator(*$1, $2, *$3); } 152 | | term { } 153 | ; 154 | 155 | term : term TMUL factor { $$ = new NBinaryOperator(*$1, $2, *$3); } 156 | | term TDIV factor { $$ = new NBinaryOperator(*$1, $2, *$3); } 157 | | term TMOD factor { $$ = new NBinaryOperator(*$1, $2, *$3); } 158 | | term comparison factor { $$ = new NBinaryOperator(*$1, $2, *$3); } 159 | | factor { } 160 | ; 161 | 162 | comparison : TCEQ 163 | | TCNEQ 164 | | TCGT 165 | | TCLT 166 | | TCGE 167 | | TCLE 168 | | TLAND 169 | | TLOR 170 | ; 171 | 172 | bitwise : TBAND 173 | | TBXOR 174 | | TBOR 175 | ; 176 | 177 | factor : var_access { } 178 | | list { } 179 | | value { } 180 | | identifier TLPAREN func_call_arg_list TRPAREN { $$ = new NFunctionCall(*$1, *$3); } 181 | | TLPAREN expression TRPAREN { $$ = $2; } 182 | | TSUB TLPAREN expression TRPAREN %prec TUMINUS { NInt *zero = new NInt(0); zero->type = *(new Type(TTINT)); $$ = new NBinaryOperator(*zero, $1, *$3); } 183 | ; 184 | 185 | var_access : identifier { $$ = new NVariableAccess(*$1); } 186 | | list_access { } 187 | | struct { } 188 | | TSUB var_access %prec TUMINUS { NInt *zero = new NInt(0); zero->type = *(new Type(TTINT)); $$ = new NBinaryOperator(*zero, $1, *$2); } 189 | ; 190 | 191 | identifier : TIDENTIFIER { std::string str = $1->c_str(); $$ = new NIdentifier(str); delete $1; } 192 | ; 193 | 194 | list_access : identifier TLBRAC expression TRBRAC { $$ = new NListAccess(*$1, $3); } /* Array access */ 195 | | identifier TLBRAC TRBRAC { $$ = new NListAccess(*$1, NULL); } /* Array append */ 196 | ; 197 | 198 | struct : identifier TPERIOD identifier { $$ = new NStructureAccess(*$1, *$3); } /* Structure access */ 199 | ; 200 | 201 | list : TLBRAC func_call_arg_list TRBRAC { $$ = new NList(*$2); } 202 | ; 203 | 204 | func_call_arg_list : /* Empty */ { $$ = new ExpressionList(); } 205 | | expression { $$ = new ExpressionList(); $$->push_back($1); } 206 | | func_call_arg_list TCOMMA expression { $$->push_back($3); } 207 | ; 208 | 209 | value : numeric { $$ = $1; } 210 | | TCHAR { $$ = new NChar(*$1); $$->type = *(new Type(TTCHAR)); delete $1; } 211 | | TSTRING { std::string str = $1->c_str(); $$ = new NString(str); $$->type = *(new Type(TTCHAR, true)); delete $1; } 212 | | TTRUE { $$ = new NBool(true); $$->type = *(new Type(TTBOOL)); } 213 | | TFALSE { $$ = new NBool(false); $$->type = *(new Type(TTBOOL)); } 214 | ; 215 | 216 | numeric : TDOUBLE { $$ = new NDouble(atof($1->c_str())); $$->type = *(new Type(TTDOUBLE)); delete $1; } 217 | | TSUB TDOUBLE %prec TUMINUS { NDouble *zero = new NDouble(0); 218 | zero->type = *(new Type(TTDOUBLE)); 219 | NDouble *d = new NDouble(atof($2->c_str())); 220 | d->type = Type(TTDOUBLE); 221 | delete $2; $$ = new NBinaryOperator(*zero, $1, *d); } 222 | | TINT { $$ = new NInt(atol($1->c_str())); $$->type = *(new Type(TTINT)); delete $1; } 223 | | TSUB TINT %prec TUMINUS { NInt *zero = new NInt(0); 224 | zero->type = TTINT; 225 | NInt *i = new NInt(atol($2->c_str())); 226 | i->type = *(new Type(TTINT)); 227 | delete $2; 228 | $$ = new NBinaryOperator(*zero, $1, *i); } 229 | ; 230 | 231 | %% 232 | -------------------------------------------------------------------------------- /src/ast.h: -------------------------------------------------------------------------------- 1 | /** 2 | @file ast.h 3 | @brief Header file storing the AST class definitions 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | Stores the class definitions for all AST-related classes and the definitions for 8 | walking the AST for semantic analysis. All AST classes are derived from Node 9 | */ 10 | 11 | #ifndef CREMA_AST_H_ 12 | #define CREMA_AST_H_ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "decls.h" 19 | #include "types.h" 20 | #include "codegen.h" 21 | 22 | extern int yylineno; 23 | 24 | /** 25 | * The base class containing all the language constructs. */ 26 | class Node { 27 | public: 28 | int lineno; 29 | Node() { lineno = yylineno; } 30 | virtual ~Node() { } 31 | virtual llvm::Value * codeGen(CodeGenContext & context) { } 32 | virtual std::ostream & print(std::ostream & os) const { }; 33 | virtual bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration *func) { }; 34 | virtual bool semanticAnalysis(SemanticContext *ctx) { }; 35 | friend std::ostream & operator<<(std::ostream & os, const Node & node); 36 | }; 37 | 38 | /** 39 | * An expression is something that evaluates to a value. 40 | * (Ex. 45*3 % 20) */ 41 | class NExpression : virtual public Node { 42 | public: 43 | Type & type; /**< Expression type used for type-checking during semantic analysis */ 44 | NExpression() : type(*(new Type())) { } 45 | virtual Type & getType(SemanticContext * ctx) const { } 46 | bool semanticAnalysis(SemanticContext * ctx) { std::cout << "Generic expression SA!" << std::endl; return false; } 47 | }; 48 | 49 | /** 50 | * A statement is a line of code that does something. In other words, 51 | * you can't pass a statement as a parameter. 52 | * (Ex. if (x==1) print("yes") else print("no")) */ 53 | class NStatement : virtual public Node { 54 | }; 55 | 56 | /** 57 | * One or more statements that evaluate to an NExpression value. */ 58 | class NBlock : public NExpression { 59 | public: 60 | StatementList statements; /**< Vector of statements in the NBlock */ 61 | llvm::Value * codeGen(CodeGenContext & context); 62 | std::ostream & print(std::ostream & os) const; 63 | void createStdlib(); 64 | bool semanticAnalysis(SemanticContext *ctx); 65 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration *func); 66 | }; 67 | 68 | /** 69 | * Statement assigning an expression to a variable */ 70 | class NAssignmentStatement : public NStatement { 71 | public: 72 | NIdentifier & ident; /**< Variable identifier */ 73 | NExpression & expr; /**< Expression to assign to variable */ 74 | NAssignmentStatement(NIdentifier & ident, NExpression & expr) : ident(ident), expr(expr) { } /**< Default constructor to create an NAssignmentStatement with the passed identifier and expression */ 75 | llvm::Value * codeGen(CodeGenContext & context); 76 | bool semanticAnalysis(SemanticContext * ctx); 77 | std::ostream & print(std::ostream & os) const; 78 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return expr.checkRecursion(ctx, func); } 79 | }; 80 | 81 | /** 82 | * Statement assigning an expression to a list element */ 83 | class NListAssignmentStatement : public NAssignmentStatement { 84 | public: 85 | NListAccess & list; /**< NListAccess for storing information about list index */ 86 | NListAssignmentStatement(NIdentifier & ident, NListAccess & list, NExpression & expr) : NAssignmentStatement(ident, expr), list(list) { } 87 | bool semanticAnalysis(SemanticContext * ctx); 88 | std::ostream & print(std::ostream & os) const; 89 | llvm::Value * codeGen(CodeGenContext & context); 90 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return expr.checkRecursion(ctx, func); } 91 | }; 92 | 93 | /** 94 | * Statement assigning an expression to a struct member */ 95 | class NStructureAssignmentStatement : public NAssignmentStatement { 96 | public: 97 | NStructureAccess & structure; /**< NStructureAccess for storing information about struct member(s) */ 98 | NStructureAssignmentStatement(NIdentifier & ident, NStructureAccess & structure, NExpression & expr) : NAssignmentStatement(ident, expr), structure(structure) { } 99 | llvm::Value * codeGen(CodeGenContext & context); 100 | std::ostream & print(std::ostream & os) const; 101 | bool semanticAnalysis(SemanticContext * ctx); 102 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return expr.checkRecursion(ctx, func); } 103 | }; 104 | 105 | /** 106 | * Looping construct */ 107 | class NLoopStatement : public NStatement { 108 | public: 109 | NIdentifier & list; /**< List variable name to loop through */ 110 | NIdentifier & asVar; /**< Temporary variable name inside of loop block to reference current list element */ 111 | NBlock & loopBlock; /**< NBlock to execute in the loop */ 112 | NLoopStatement(NIdentifier & list, NIdentifier & asVar, NBlock & loopBlock) : list(list), asVar(asVar), loopBlock(loopBlock) { } 113 | std::ostream & print(std::ostream & os) const; 114 | llvm::Value * codeGen(CodeGenContext & context); 115 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return loopBlock.checkRecursion(ctx, func); } 116 | bool semanticAnalysis(SemanticContext * ctx); 117 | }; 118 | 119 | /** 120 | * If statement (can include elseif and else) */ 121 | class NIfStatement : public NStatement { 122 | public: 123 | NExpression & condition; /**< Expression to evaluate to determine if the conditional will execute */ 124 | NBlock & thenblock; /**< NBlock to execute if the condition is true */ 125 | NBlock * elseblock; /**< Optional pointer to NBlock to execute if the condition is false */ 126 | NStatement * elseif; /**< Optional pointer to NBlock to execute if the elseif condition is true */ 127 | NIfStatement(NExpression & condition, NBlock & thenblock, NBlock * elseblock) : condition(condition), thenblock(thenblock), elseblock(elseblock), elseif(NULL) { } 128 | NIfStatement(NExpression & condition, NBlock & thenblock, NStatement * elseif) : condition(condition), thenblock(thenblock), elseblock(NULL), elseif(elseif) { } 129 | NIfStatement(NExpression & condition, NBlock & thenblock) : condition(condition), thenblock(thenblock), elseblock(NULL), elseif(NULL) { } 130 | llvm::Value * codeGen(CodeGenContext & context); 131 | std::ostream & print(std::ostream & os) const; 132 | bool semanticAnalysis(SemanticContext * ctx); 133 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return thenblock.checkRecursion(ctx, func) || (elseblock ? elseblock->checkRecursion(ctx, func) : false) || (elseif ? elseif->checkRecursion(ctx, func) : false); } 134 | }; 135 | 136 | /** 137 | * Binary operator expression (e.g. lhs + rhs) */ 138 | class NBinaryOperator : public NExpression { 139 | public: 140 | int op; /**< Binary operator to execute on the lhs and rhs */ 141 | NExpression & lhs; /**< Left hand-side of binary operator */ 142 | NExpression & rhs; /**< Right hand-side of binary operator */ 143 | NBinaryOperator(NExpression & lhs, int op, NExpression & rhs) : lhs(lhs), op(op), rhs(rhs) { } 144 | llvm::Value * codeGen(CodeGenContext & context); 145 | bool semanticAnalysis(SemanticContext *ctx); 146 | Type & getType(SemanticContext * ctx) const; 147 | std::ostream & print(std::ostream & os) const; 148 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return lhs.checkRecursion(ctx, func) || rhs.checkRecursion(ctx, func); } 149 | }; 150 | 151 | /** 152 | * Declaring a variable and its type, optionally with an expression for its initial value */ 153 | class NVariableDeclaration : public NStatement { 154 | public: 155 | Type & type; /**< Type of variable */ 156 | NIdentifier & ident; /**< Name of variable */ 157 | NExpression *initializationExpression; /**< Pointer to optional initialization expression */ 158 | NVariableDeclaration(Type & type, NIdentifier & name) : type(type), ident(name), initializationExpression(NULL) { } 159 | NVariableDeclaration(Type & type, NIdentifier & name, NExpression *initExpr) : type(type), ident(name), initializationExpression(initExpr) { } 160 | llvm::Value * codeGen(CodeGenContext & context); 161 | std::ostream & print(std::ostream & os) const; 162 | bool semanticAnalysis(SemanticContext *ctx); 163 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return initializationExpression ? initializationExpression->checkRecursion(ctx, func) : false; } 164 | }; 165 | 166 | /** 167 | * Declares a function */ 168 | class NFunctionDeclaration : public NStatement { 169 | public: 170 | VariableList variables; /**< List of variables to take as arguments */ 171 | NIdentifier & ident; /**< Name of function */ 172 | Type & type; /**< Return type of function */ 173 | NBlock *body; /**< NBlock body of function */ 174 | NFunctionDeclaration(Type & type, NIdentifier & ident, VariableList & variables, NBlock *body) : type(type), ident(ident), variables(variables), body(body) { } 175 | llvm::Value * codeGen(CodeGenContext & context); 176 | bool semanticAnalysis(SemanticContext * ctx); 177 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return false; } 178 | std::ostream & print(std::ostream & os) const; 179 | }; 180 | 181 | /** 182 | * Declares a structure */ 183 | class NStructureDeclaration : public NStatement { 184 | public: 185 | VariableList members; /**< List of variable members of structure */ 186 | NIdentifier & ident; /**< Name of structure */ 187 | NStructureDeclaration(NIdentifier & ident, VariableList & members) : ident(ident), members(members) { } 188 | llvm::Value * codeGen(CodeGenContext & context); 189 | std::ostream & print(std::ostream & os) const; 190 | bool semanticAnalysis(SemanticContext * ctx); 191 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return false; } 192 | }; 193 | 194 | /** 195 | * Expression resolving to a function call */ 196 | class NFunctionCall : public NExpression, public NStatement { 197 | public: 198 | ExpressionList args; /**< List of function call arguments */ 199 | NIdentifier & ident; /**< Name of function to call */ 200 | NFunctionCall(NIdentifier & ident, ExpressionList & args) : ident(ident), args(args) { } 201 | llvm::Value * codeGen(CodeGenContext & context); 202 | Type & getType(SemanticContext * ctx) const; 203 | std::ostream & print(std::ostream & os) const; 204 | bool semanticAnalysis(SemanticContext * ctx); 205 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func); 206 | }; 207 | 208 | /** 209 | * Expression resolving to a structure access */ 210 | class NStructureAccess : public NExpression { 211 | public: 212 | NIdentifier & member; /**< Structure member name to access */ 213 | NIdentifier & ident; /**< Name of structure variable */ 214 | NStructureAccess(NIdentifier & ident, NIdentifier & member) : ident(ident), member(member) { } 215 | std::ostream & print(std::ostream & os) const; 216 | Type & getType(SemanticContext * ctx) const; 217 | llvm::Value * codeGen(CodeGenContext & context); 218 | bool semanticAnalysis(SemanticContext * ctx); 219 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return false; } 220 | }; 221 | 222 | /** 223 | * Expression resolving to list access */ 224 | class NListAccess : public NExpression { 225 | public: 226 | NExpression * index; /**< NExpression that resolves to list index */ 227 | NIdentifier & ident; /**< Name of list variable to access */ 228 | NListAccess(NIdentifier & ident, NExpression * index) : ident(ident), index(index) { } 229 | std::ostream & print(std::ostream & os) const; 230 | llvm::Value * codeGen(CodeGenContext & context); 231 | Type & getType(SemanticContext * ctx) const; 232 | bool semanticAnalysis(SemanticContext * ctx); 233 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return false; } 234 | }; 235 | 236 | /** 237 | * Expression resolving to a singleton variable access */ 238 | class NVariableAccess : public NExpression { 239 | public: 240 | NIdentifier & ident; /**< Name or variable to access */ 241 | NVariableAccess(NIdentifier & ident) : ident(ident) { } 242 | std::ostream & print(std::ostream & os) const; 243 | llvm::Value * codeGen(CodeGenContext & context); 244 | Type & getType(SemanticContext * ctx) const; 245 | bool semanticAnalysis(SemanticContext * ctx); 246 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return false; } 247 | }; 248 | 249 | /** 250 | * Return statement */ 251 | class NReturn : public NStatement { 252 | public: 253 | NExpression & retExpr; /**< NExpression to return to parent scope */ 254 | NReturn(NExpression & re) : retExpr(re) { } 255 | llvm::Value * codeGen(CodeGenContext & context); 256 | bool semanticAnalysis(SemanticContext * ctx); 257 | std::ostream & print(std::ostream & os) const; 258 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return retExpr.checkRecursion(ctx, func); } 259 | }; 260 | 261 | /** 262 | * Return statement */ 263 | class NBreak : public NStatement { 264 | public: 265 | NBreak() { } 266 | llvm::Value * codeGen(CodeGenContext & context); 267 | bool semanticAnalysis(SemanticContext * ctx); 268 | std::ostream & print(std::ostream & os) const; 269 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return false; } 270 | }; 271 | 272 | /** 273 | * Base class for values (ints, strings, doubles, lists, etc.) */ 274 | class NValue : public NExpression { 275 | public: 276 | virtual llvm::Value* codeGen(CodeGenContext & context) { } 277 | std::ostream & print(std::ostream & os) const; 278 | Type & getType(SemanticContext * ctx) const { return type; } 279 | bool semanticAnalysis(SemanticContext * ctx) { return true; } 280 | bool checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) { return false; } 281 | }; 282 | 283 | /** 284 | * Double */ 285 | class NDouble : public NValue { 286 | public: 287 | double value; /**< Value of double */ 288 | NDouble(double value) : value(value) { } 289 | llvm::Value * codeGen(CodeGenContext & context); 290 | std::ostream & print(std::ostream & os) const; 291 | }; 292 | 293 | /** 294 | * Unsigned int */ 295 | class NUInt : public NValue { 296 | public: 297 | unsigned long int value; /**< Value of unsigned int */ 298 | NUInt(unsigned long int value) : value(value) { } 299 | llvm::Value* codeGen(CodeGenContext & context); 300 | std::ostream & print(std::ostream & os) const; 301 | }; 302 | 303 | /** 304 | * Signed int */ 305 | class NInt : public NValue { 306 | public: 307 | long int value; /**< Value of signed int */ 308 | NInt(long int value) : value(value) { } 309 | llvm::Value* codeGen(CodeGenContext & context); 310 | std::ostream & print(std::ostream & os) const; 311 | }; 312 | 313 | /** 314 | * Char */ 315 | class NChar : public NValue { 316 | public: 317 | char value; /**< Value of signed int */ 318 | NChar(char value) : value(value) { } 319 | NChar(std::string str); 320 | llvm::Value* codeGen(CodeGenContext & context); 321 | std::ostream & print(std::ostream & os) const; 322 | }; 323 | 324 | /** 325 | * String type */ 326 | class NString : public NValue { 327 | public: 328 | std::string value; /**< Value of string */ 329 | NString(std::string & ivalue) { ivalue.erase(0, 1); ivalue.erase(ivalue.length() - 1, 1); value = ivalue; } 330 | llvm::Value * codeGen(CodeGenContext & context); 331 | std::ostream & print(std::ostream & os) const; 332 | }; 333 | 334 | /** 335 | * Bool type */ 336 | class NBool : public NValue { 337 | public: 338 | bool value; /**< Value of bool */ 339 | NBool(bool value) : value(value) { } 340 | llvm::Value * codeGen(CodeGenContext & context); 341 | std::ostream & print(std::ostream & os) const; 342 | }; 343 | 344 | /** 345 | * List type */ 346 | class NList : public NValue { 347 | public: 348 | ExpressionList value; /**< List of NExpression elements */ 349 | NList() { } /**< Default constructor to initialize an empty list */ 350 | NList(ExpressionList & list) : value(list) { } 351 | Type & getType(SemanticContext * ctx) const; 352 | llvm::Value* codeGen(CodeGenContext & context); 353 | std::ostream & print(std::ostream & os) const; 354 | bool semanticAnalysis(SemanticContext * ctx); 355 | }; 356 | 357 | /** 358 | * Identifier */ 359 | class NIdentifier : public NExpression { 360 | public: 361 | std::string value; /**< Identifier value */ 362 | NIdentifier(const std::string & value) : value(value) { } 363 | virtual llvm::Value* codeGen(CodeGenContext & context) { } 364 | std::ostream & print(std::ostream & os) const; 365 | friend bool operator==(const NIdentifier & i1, const NIdentifier & i2); 366 | }; 367 | 368 | 369 | 370 | #endif // CREMA_AST_H_ 371 | -------------------------------------------------------------------------------- /src/stdlib/stdlib.c: -------------------------------------------------------------------------------- 1 | /** 2 | @file stdlib.c 3 | @brief Funcutionality for standard C routines that support Crema programs 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | A source file defining functions and other structures needed to support 8 | in the execution of Crema programs 9 | */ 10 | #include "stdlib.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | //#define KLEE 18 | 19 | #ifdef KLEE 20 | #include "klee/klee.h" 21 | #endif 22 | 23 | /* 24 | Re-allocates memory for a list. 25 | 26 | @param list The list to re-allocate memory for 27 | @param new_sz The number of bytes to be re-allocated for the list 28 | */ 29 | static void list_resize(list_t * list, size_t new_sz) 30 | { 31 | if (list == NULL) 32 | { 33 | return; 34 | } 35 | if (new_sz > list->len) 36 | { 37 | list->arr = realloc(list->arr, new_sz * list->elem_sz); 38 | list->cap = new_sz; 39 | } 40 | } 41 | 42 | /* 43 | Allocates a new list_t structure, which represents either an array or a string 44 | in a Crema program. 45 | 46 | @param es The number of byes each element fo the list takes (i.e. 1 for char, 8 for double, etc) 47 | */ 48 | list_t * list_create(int64_t es) 49 | { 50 | list_t * l = malloc(sizeof(list_t)); 51 | if (l) 52 | { 53 | l->elem_sz = es; 54 | l->cap = 0; 55 | l->len = 0; 56 | l->arr = NULL; 57 | } 58 | return l; 59 | } 60 | 61 | /* 62 | Frees memory for the given list_t structure 63 | 64 | @param list The list_t structure to be freed 65 | */ 66 | void list_free(list_t * list) 67 | { 68 | if (list == NULL) 69 | { 70 | return; 71 | } 72 | if (list->arr != NULL) 73 | { 74 | free(list->arr); 75 | } 76 | free(list); 77 | } 78 | 79 | /* 80 | Removes an element from a list at a given index 81 | 82 | @param list The list to be operated on 83 | @param idx The index of the element to be removed from the list 84 | */ 85 | void list_delete(list_t * list, unsigned int idx) 86 | { 87 | if (list == NULL) 88 | { 89 | return; 90 | } 91 | if (idx < list->len) 92 | { 93 | memmove(list->arr + (idx * list->elem_sz), list->arr + ((idx + 1) * list->elem_sz), (list->len - idx - 1) * list->elem_sz); 94 | list->len--; 95 | } 96 | } 97 | 98 | /* 99 | Returns the number of elements in a list_t structure (string or array from a Crema program) 100 | 101 | @return The length of the given list 102 | */ 103 | int64_t list_length(list_t * list) 104 | { 105 | return list->len; 106 | } 107 | 108 | /* 109 | Removes a character from a string at a given index 110 | 111 | @param str The string to be operated on 112 | @param idx The index of the character to be removed from the string 113 | */ 114 | void str_delete(string_t * str, unsigned int idx) 115 | { 116 | list_delete(str, idx); 117 | ((char *) str->arr)[str->len] = '\0'; 118 | } 119 | 120 | /* 121 | Inserts an element into a list_t structure at the given index 122 | 123 | @param list Pointer to a list_t structure 124 | */ 125 | void list_insert(list_t * list, unsigned int idx, void * elem) 126 | { 127 | if (list == NULL) 128 | { 129 | return; 130 | } 131 | if (idx < list->len) 132 | { 133 | memcpy(list->arr + (idx * list->elem_sz), elem, list->elem_sz); 134 | } 135 | } 136 | 137 | /* 138 | Returns a pointer to the element of a list_t structure found at the given index 139 | 140 | @param list Pointer to a list_t structure 141 | @param idx The index to retrieve from list 142 | @return Pointer to the element at the given index 143 | */ 144 | void * list_retrieve(list_t * list, unsigned int idx) 145 | { 146 | if (list == NULL) 147 | { 148 | return NULL; 149 | } 150 | if (idx >= list->len) 151 | { 152 | return NULL; 153 | } 154 | return list->arr + (idx * list->elem_sz); 155 | } 156 | 157 | /* 158 | Appends a new element onto the given list, increasing the size of the list by one 159 | 160 | @param list Pointer to a list 161 | @param elem Pointer to and element to be appended onto the list 162 | */ 163 | void list_append(list_t * list, void * elem) 164 | { 165 | if (list == NULL) 166 | { 167 | return; 168 | } 169 | // use len+1 to save space for a terminating entry (e.g. '\0') 170 | if (list->len+1 >= list->cap) 171 | { 172 | list_resize(list, list->cap + DEFAULT_RESIZE_AMT); 173 | } 174 | list->len++; 175 | list_insert(list, list->len - 1, elem); 176 | } 177 | 178 | /* 179 | Concatenates two lists together (string or array) 180 | 181 | @param list1 The first list 182 | @param list2 The second list to be concatenated onto the first list 183 | */ 184 | void list_concat(list_t * list1, list_t * list2) 185 | { 186 | int i; 187 | if (list1->elem_sz != list2->elem_sz) 188 | { 189 | return; 190 | } 191 | list_resize(list1, list1->cap + list2->len); 192 | for (i = 0; i < list2->len; i++) 193 | { 194 | list_append(list1, list_retrieve(list2, i)); 195 | } 196 | } 197 | 198 | /* 199 | Creates a list of characters (a string) 200 | 201 | @return list A new empty list of characters 202 | */ 203 | string_t * str_create() 204 | { 205 | return list_create(sizeof(char)); 206 | } 207 | 208 | /* 209 | Converts a null-termiated C style string into a 210 | list of characters (a Crema string) 211 | 212 | @param s Pointer to a C character array 213 | @return Returns the list_t equivalent of the given C string 214 | */ 215 | string_t * str_from_cstring(char * s) 216 | { 217 | string_t * str = list_create(sizeof(char)); 218 | list_resize(str, strlen(s) + 1); 219 | strncpy(str->arr, s, strlen(s)); 220 | str->len = strlen(s); 221 | str_insert(str, strlen(s), '\0'); 222 | return str; 223 | } 224 | 225 | /* 226 | Returns a sub-string of a given string, given by a start index within the 227 | string, and a length of the substring. 228 | 229 | @param str The string to be operated on 230 | @param start The starting index of the substring 231 | @param len The number of characters after 'start' to be included in the substring 232 | @return The substring 233 | */ 234 | string_t * str_substr(string_t * str, unsigned int start, unsigned int len) 235 | { 236 | string_t * nstr = list_create(sizeof(char)); 237 | 238 | if (start >= str->len) 239 | return NULL; 240 | 241 | if (len > str->len || len == 0) 242 | len = str->len - start; 243 | 244 | if (start == 0 && (len == 0 || len == str->len)) 245 | return str; 246 | 247 | if (start >= str->len) 248 | return NULL; 249 | 250 | list_resize(nstr, len + 1); 251 | strncpy(nstr->arr, (char*)str->arr + start, len); 252 | nstr->len = len; 253 | str_insert(nstr, len, '\0'); 254 | return nstr; 255 | } 256 | 257 | /* 258 | Alias for list_free() 259 | */ 260 | void str_free(string_t * str) 261 | { 262 | list_free(str); 263 | } 264 | 265 | /* 266 | Alias for list_insert() 267 | */ 268 | void str_insert(string_t * str, unsigned int idx, char elem) 269 | { 270 | list_insert(str, idx, (void *) &elem); 271 | } 272 | 273 | /* 274 | Retrieves the character in the given string at the given index 275 | 276 | @param str The string to be operated on 277 | @param idx The index of the character to be returned 278 | @return The character found in string str at index idx 279 | */ 280 | char str_retrieve(string_t * str, unsigned int idx) 281 | { 282 | char * p = list_retrieve(str, idx); 283 | if (p == NULL) 284 | { 285 | fprintf(stderr, "ERROR: Retrieving out of bounds list element!\n"); 286 | exit(-1); 287 | } 288 | return (char) *p; 289 | } 290 | 291 | /* 292 | Appends a new character onto the given string, increasing the size of the, 293 | list by one, and appends a null-terminating character ('\0') at the end of the string 294 | 295 | @param list Pointer to a list 296 | @param elem Pointer to and element to be appended onto the list 297 | */ 298 | void str_append(string_t * str, char elem) 299 | { 300 | list_append(str, (void *) &elem); 301 | ((char*)str->arr)[str->len] = '\0'; 302 | } 303 | 304 | /* 305 | Concatenates two strings together, and then appends a null-termiating 306 | character ('\0') to the result 307 | 308 | @param str1 The first string 309 | @param str2 The second string to be concatenated onto the first list 310 | */ 311 | void str_concat(string_t * str1, string_t * str2) 312 | { 313 | list_concat(str1, str2); 314 | ((char*)str1->arr)[str1->len] = '\0'; 315 | } 316 | 317 | /* 318 | Prints a string without a new line character 319 | 320 | @param str The string to be printed 321 | */ 322 | void str_print(string_t * str) 323 | { 324 | if(str->arr == NULL) 325 | { 326 | return; 327 | } 328 | printf("%s", (char *) str->arr); 329 | } 330 | 331 | /* 332 | Prints a string with a new line character 333 | 334 | @param str The string to be printed 335 | */ 336 | void str_println(string_t * str) 337 | { 338 | if(str->arr == NULL) 339 | { 340 | printf("\n"); 341 | } 342 | else 343 | { 344 | printf("%s\n", (char *) str->arr); 345 | } 346 | } 347 | 348 | /* 349 | Creates an empty list of type int 350 | 351 | @return Pointer to the intialized list 352 | */ 353 | list_t * int_list_create() 354 | { 355 | return list_create(sizeof(int64_t)); 356 | } 357 | 358 | /* 359 | Inserts a value into a list of type int at the given index 360 | 361 | @param list The list to insert into 362 | @param idx The index in the list to insert the value 363 | @param val The int value to be inserted 364 | */ 365 | void int_list_insert(list_t * list, int64_t idx, int64_t val) 366 | { 367 | list_insert(list, idx, (void *) &val); 368 | } 369 | 370 | /* 371 | Retrieves the element from a list of type int at the given index 372 | 373 | @param list The list to search 374 | @param idx The index of the element to be retrieved 375 | @return The element (int value) at the given index 376 | */ 377 | int64_t int_list_retrieve(list_t * list, int64_t idx) 378 | { 379 | int64_t *p = list_retrieve(list, idx); 380 | if (p == NULL) 381 | { 382 | fprintf(stderr, "ERROR: Retrieving out of bounds list element!\n"); 383 | exit(-1); 384 | } 385 | return (int64_t) *p; 386 | } 387 | 388 | /* 389 | Appends a new element of type int onto the given list, increasing the size of the list by one 390 | 391 | @param list A list to append an int to 392 | @param elem The int to be appended 393 | */ 394 | void int_list_append(list_t * list, int64_t elem) 395 | { 396 | list_append(list, (void *) &elem); 397 | } 398 | 399 | /* 400 | Creates an empty list of type int 401 | 402 | @return Pointer to the intialized list 403 | */ 404 | list_t * double_list_create() 405 | { 406 | return list_create(sizeof(double)); 407 | } 408 | 409 | /* 410 | Inserts a value into a list of type double at the given index 411 | 412 | @param list The list to insert into 413 | @param idx The index in the list to insert the value 414 | @param val The double value to be inserted 415 | */ 416 | void double_list_insert(list_t * list, unsigned int idx, double val) 417 | { 418 | list_insert(list, idx, (void *) &val); 419 | } 420 | 421 | /* 422 | Retrieves the element from a list of type double at the given index 423 | 424 | @param list The list to search 425 | @param idx The index of the element to be retrieved 426 | @return The element (double value) at the given index 427 | */ 428 | double double_list_retrieve(list_t * list, unsigned int idx) 429 | { 430 | double *p = list_retrieve(list, idx); 431 | if (p != NULL) 432 | { 433 | fprintf(stderr, "ERROR: Retrieving out of bounds list element!\n"); 434 | exit(-1); 435 | } 436 | return (double) *p; 437 | } 438 | 439 | /* 440 | Appends a new element of type double onto the given list, increasing the size of the list by one 441 | 442 | @param list A list to append an double to 443 | @param elem The double to be appended 444 | */ 445 | void double_list_append(list_t * list, double elem) 446 | { 447 | list_append(list, (void *) &elem); 448 | } 449 | 450 | /* 451 | Generates a linear sequence of int values in the range of start to end, 452 | and returns them as an array 453 | 454 | @param start An integer number to begin a sequence at 455 | @param end An integer number to end the sequence at 456 | */ 457 | list_t * crema_seq(int64_t start, int64_t end) 458 | { 459 | list_t * l; 460 | int64_t i; 461 | if (end <= start) 462 | { 463 | return NULL; 464 | } 465 | l = int_list_create(); 466 | list_resize(l, end - start); 467 | for (i = start; i <= end; i++) 468 | { 469 | int_list_append(l, i); 470 | } 471 | return l; 472 | } 473 | 474 | /** 475 | Prints a double value to stdout. 476 | 477 | @params val A double value to be printed 478 | */ 479 | void double_print(double val) 480 | { 481 | printf("%lf", val); 482 | } 483 | 484 | /** 485 | Prints a double value to stdout terminated with a new-line character 486 | 487 | @params val A double value to be printed 488 | */ 489 | void double_println(double val) 490 | { 491 | printf("%lf\n", val); 492 | } 493 | 494 | /** 495 | Prints a int value to stdout. 496 | 497 | @params val An int value to be printed 498 | */ 499 | void int_print(int64_t val) 500 | { 501 | printf("%ld", val); 502 | } 503 | 504 | /** 505 | Prints an int value to stdout terminated with a new-line character 506 | 507 | @params val An int value to be printed 508 | */ 509 | void int_println(int64_t val) 510 | { 511 | printf("%ld\n", val); 512 | } 513 | 514 | /** 515 | ??? 516 | 517 | @params list ??? 518 | */ 519 | void make_symbolic(list_t * list) 520 | { 521 | #ifdef KLEE 522 | uint64_t x = klee_int("testint"); 523 | *(uint64_t *)list->arr = x; 524 | #endif 525 | } 526 | 527 | // void bool_print(bool val) 528 | // { 529 | // printf("%s", val ? "true" : "false"); 530 | // } 531 | 532 | char **main_args = NULL; 533 | int64_t main_argc = 0; 534 | 535 | /** 536 | Stores the command line arguments passed into the running program in 537 | global variables for the Crema standard lib. 538 | 539 | @params argc The argc (argument count) value to be saved 540 | @params argv The argv (argument list) values to be saved 541 | */ 542 | void save_args(int64_t argc, char ** argv) 543 | { 544 | main_args = argv; 545 | main_argc = argc; 546 | } 547 | 548 | /** 549 | Returns the number of command line arguments passed to the running Crema program 550 | 551 | @return The number of command line arguments passed 552 | */ 553 | int64_t prog_arg_count() 554 | { 555 | return main_argc; 556 | } 557 | 558 | /** 559 | Retrieves the string representation of a command line argument at a given postion 560 | from the command line arguments list passed to the running Crema program 561 | 562 | @param idx The index of the argument to be retrieved 563 | @return The command line argument (string) at position idx, or "null cstring" 564 | */ 565 | list_t * prog_argument(int64_t idx) 566 | { 567 | if (idx >= main_argc) 568 | return str_from_cstring("null cstring"); 569 | 570 | return str_from_cstring(main_args[idx]); 571 | } 572 | 573 | // *********************** Type Conversions ***************************** // 574 | 575 | /** 576 | Cast a double value as an int. 577 | 578 | @params val A double value 579 | @return The given double value cast as an int 580 | */ 581 | int64_t double_to_int(double val) 582 | { 583 | return (int64_t)val; 584 | } 585 | 586 | /** 587 | Cast an int value as a double. 588 | 589 | @params val An int value 590 | @return The given int value cast as a double 591 | */ 592 | double int_to_double(int64_t val) 593 | { 594 | return (double)val; 595 | } 596 | 597 | /** 598 | Convert an int (64-bit) value into a string of characters. 599 | 600 | @params val An 64-bit int value 601 | @return The given int value cast as a double 602 | */ 603 | string_t * int_to_string(int64_t val) 604 | { 605 | char str[20]; 606 | string_t * stp; 607 | 608 | snprintf(str, 20, "%ld", val); 609 | stp = str_from_cstring(str); 610 | return stp; 611 | } 612 | 613 | /** 614 | Converts a string to an int. 615 | 616 | @params str A string to be converted to an int 617 | @return Int value, or 0.0 if the string could not be parsed as an int 618 | */ 619 | int64_t string_to_int(string_t * str) 620 | { 621 | if(str->arr == NULL) 622 | { 623 | return 0; 624 | } 625 | else 626 | { 627 | return atoi(str->arr); 628 | } 629 | } 630 | 631 | /** 632 | Converts a string to a double. 633 | 634 | @params str A string to be converted to a double 635 | @return Double value, or 0.0 if the string could not be parsed as a double 636 | */ 637 | int64_t string_to_double(string_t * str) 638 | { 639 | if(str->arr == NULL) 640 | { 641 | return 0; 642 | } 643 | else 644 | { 645 | return atof(str->arr); 646 | } 647 | } 648 | 649 | // ***************************** Maths ********************************* // 650 | 651 | /** 652 | Calculates the nearest integral value less than the given number. 653 | 654 | @params val A double value 655 | @return The next lowest integral value 656 | */ 657 | double double_floor(double val) 658 | { 659 | // return val >= 0 ? (double)((long)val) : (double)((long)(val - 1)); 660 | return floor(val); 661 | } 662 | 663 | /** 664 | Calculates the nearest integral value greater than the given number. 665 | 666 | @params val A double value 667 | @return The next highest integral value 668 | */ 669 | double double_ceiling(double val) 670 | { 671 | return val > 0 ? (double)((long)(val + 1)) : (double)((long)val); 672 | } 673 | 674 | /** 675 | Calculates the nearest integral value to the given number. 676 | The result will be rounded up for values with a decimal portion equal 677 | to .5000... 678 | 679 | @params val A double value 680 | @return The nearest integral value 681 | */ 682 | double double_round(double val) 683 | { 684 | if(val >= 0) 685 | { 686 | return val - (long)val >= 0.5 ? (double)((long)(val + 1)) : (double)((long)val); 687 | } 688 | else 689 | { 690 | return val - (long)val >= -0.5 ? (double)((long)val) : (double)((long)(val - 1)); 691 | } 692 | } 693 | 694 | /** 695 | Truncates the given double value to an integral number. In other words, all 696 | values after the decimal point become 0. 697 | 698 | @params val A double value 699 | @return The double value after truncation 700 | */ 701 | double double_truncate(double val) 702 | { 703 | return trunc(val); 704 | } 705 | 706 | /** 707 | Calculates the square of the given double value (i.e. n*n or n^2) 708 | 709 | @params val A double value 710 | @return The double value squared 711 | */ 712 | double double_square(double val) 713 | { 714 | return val*val; 715 | } 716 | 717 | /** 718 | Calculates the square of the given int value (i.e. n*n or n^2) 719 | 720 | @params val An int value 721 | @return The int value squared 722 | */ 723 | int64_t int_square(int64_t val) 724 | { 725 | return val*val; 726 | } 727 | 728 | /** 729 | Calculates the value of a double number raised to a power (i.e. x^n) 730 | 731 | @params base The base value for the exponential 732 | @params power The power for the exponential 733 | @return The base value raised to the given power 734 | */ 735 | double double_pow(double base, double power) 736 | { 737 | return pow(base, power); 738 | } 739 | 740 | /** 741 | Calculates the value of an int number raised to a power (i.e. x^n) 742 | 743 | @params base The base value for the exponential 744 | @params power The power for the exponential 745 | @return The base value raised to the given power 746 | */ 747 | int64_t int_pow(int64_t base, int64_t power) 748 | { 749 | return (int64_t)pow(base, power); 750 | } 751 | 752 | /** 753 | Calculates the value of the trigonometric sin function of a 754 | double value (i.e. sin(n)) 755 | 756 | @params val A double value 757 | @return The sin of the given value 758 | */ 759 | double double_sin(double val) 760 | { 761 | return sin(val); 762 | } 763 | 764 | /** 765 | Calculates the value of the trigonometric cos function of a 766 | double value (i.e. cos(n)) 767 | 768 | @params val A double value 769 | @return The cos of the given value 770 | */ 771 | double double_cos(double val) 772 | { 773 | return cos(val); 774 | } 775 | 776 | /** 777 | Calculates the value of the trigonometric tan function of a 778 | double value (i.e. tan(n)) 779 | 780 | @params val A double value 781 | @return The tan of the given value 782 | */ 783 | double double_tan(double val) 784 | { 785 | return tan(val); 786 | } 787 | 788 | /** 789 | Calculates the square root for a given double value 790 | 791 | @params val A double value 792 | @return The square root of the given value 793 | */ 794 | double double_sqrt(double val) 795 | { 796 | return sqrt(val); 797 | } 798 | 799 | /** 800 | Calculates the absolute value for a given double value 801 | 802 | @params val A double value 803 | @return The absolute value of the given value 804 | */ 805 | double double_abs(double val) 806 | { 807 | return abs(val); 808 | } 809 | 810 | /** 811 | Calculates the absolute value for a given int value 812 | 813 | @params val A int value 814 | @return The absolute value of the given value 815 | */ 816 | int64_t int_abs(int64_t val) 817 | { 818 | return abs(val); 819 | } 820 | -------------------------------------------------------------------------------- /src/ast.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file ast.cpp 3 | @brief Implementation file for AST-related classes 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | Stores the implementations for the AST classes. Includes pretty-printing logic 8 | and some operator overloads for AST manipulation and analysis. 9 | */ 10 | #include "ast.h" 11 | #include "parser.h" 12 | #include "types.h" 13 | #include "semantics.h" 14 | 15 | /** 16 | Overload for the == operator to allow for simple comparison of two NIdentifiers. 17 | The NIdentifier values being compared are of type string, which represent the names of 18 | variables, lists, structs, struct members, etc. 19 | 20 | @param i1 First NIdentifier to compare 21 | @param i2 Second NIdentifier to compare 22 | @return true if the Identifiers resolve to the same value, false otherwise 23 | */ 24 | bool operator==(const NIdentifier & i1, const NIdentifier & i2) 25 | { 26 | return i1.value == i2.value; 27 | } 28 | 29 | /** 30 | Overload for the << operator to allow pretty printing of AST objects and AST as a whole 31 | 32 | @param os Output stream to print to 33 | @param node Node to pretty-print 34 | @return Output stream passed in 35 | */ 36 | std::ostream & operator<<(std::ostream & os, const Node & node) 37 | { 38 | node.print(os); 39 | return os; 40 | } 41 | 42 | static inline NFunctionDeclaration * generateFuncDecl(Type & type, std::string name, std::vector args) 43 | { 44 | NIdentifier * ident = new NIdentifier(name); 45 | return new NFunctionDeclaration(type, *ident, args, NULL); 46 | } 47 | 48 | /** 49 | Function to define certain standard library declarations 50 | */ 51 | void NBlock::createStdlib() 52 | { 53 | std::vector args; 54 | NFunctionDeclaration *func; 55 | Type * ct = new Type(); 56 | ct->typecode = CHAR; 57 | ct->isList = false; 58 | // int_list_create() 59 | func = generateFuncDecl(*(new Type(TTINT, true)), "int_list_create", args); 60 | statements.insert(statements.begin(), func); 61 | rootCtx.registerFunc(func); 62 | 63 | // double_list_create() 64 | func = generateFuncDecl(*(new Type(TTDOUBLE, true)), "double_list_create", args); 65 | statements.insert(statements.begin(), func); 66 | rootCtx.registerFunc(func); 67 | 68 | // str_create() 69 | func = generateFuncDecl(*(new Type(*ct, true)), "str_create", args); 70 | statements.insert(statements.begin(), func); 71 | rootCtx.registerFunc(func); 72 | 73 | // list_length(list) 74 | args.push_back(new NVariableDeclaration(*(new Type(TTINT, true)), *(new NIdentifier("l")))); 75 | func = generateFuncDecl(*(new Type(TTINT)), "list_length", args); 76 | statements.insert(statements.begin(), func); 77 | rootCtx.registerFunc(func); 78 | 79 | // int_list_retrieve(list, idx) 80 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("idx")))); 81 | func = generateFuncDecl(*(new Type(TTINT)), "int_list_retrieve", args); 82 | statements.insert(statements.begin(), func); 83 | rootCtx.registerFunc(func); 84 | 85 | // str_retrieve(list, idx) 86 | func = generateFuncDecl(*(new Type(TTCHAR)), "str_retrieve", args); 87 | statements.insert(statements.begin(), func); 88 | rootCtx.registerFunc(func); 89 | 90 | // double_list_retrieve(list, idx) 91 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_list_retrieve", args); 92 | statements.insert(statements.begin(), func); 93 | rootCtx.registerFunc(func); 94 | 95 | // int_list_append(list, val) 96 | func = generateFuncDecl(*(new Type(TTVOID)), "int_list_append", args); 97 | statements.insert(statements.begin(), func); 98 | rootCtx.registerFunc(func); 99 | 100 | // int_list_insert(list, idx, val) 101 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("val")))); 102 | func = generateFuncDecl(*(new Type(TTVOID)), "int_list_insert", args); 103 | statements.insert(statements.begin(), func); 104 | rootCtx.registerFunc(func); 105 | 106 | // double_list_append(l, val) 107 | args.clear(); 108 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE, true)), *(new NIdentifier("l")))); 109 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 110 | func = generateFuncDecl(*(new Type(TTVOID)), "double_list_append", args); 111 | statements.insert(statements.begin(), func); 112 | rootCtx.registerFunc(func); 113 | 114 | // double_list_insert(l, idx, val) 115 | args.clear(); 116 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE, true)), *(new NIdentifier("l")))); 117 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("idx")))); 118 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 119 | func = generateFuncDecl(*(new Type(TTVOID)), "double_list_insert", args); 120 | statements.insert(statements.begin(), func); 121 | rootCtx.registerFunc(func); 122 | 123 | // double_print 124 | args.clear(); 125 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 126 | func = generateFuncDecl(*(new Type(TTVOID)), "double_print", args); 127 | statements.insert(statements.begin(), func); 128 | rootCtx.registerFunc(func); 129 | 130 | // double_println 131 | args.clear(); 132 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 133 | func = generateFuncDecl(*(new Type(TTVOID)), "double_println", args); 134 | statements.insert(statements.begin(), func); 135 | rootCtx.registerFunc(func); 136 | 137 | // str_print(list) & str_println(list) 138 | args.clear(); 139 | args.push_back(new NVariableDeclaration(*(new Type(TTCHAR, true)), *(new NIdentifier("l")))); 140 | func = generateFuncDecl(*(new Type(TTVOID)), "str_print", args); 141 | statements.insert(statements.begin(), func); 142 | rootCtx.registerFunc(func); 143 | func = generateFuncDecl(*(new Type(TTVOID)), "str_println", args); 144 | statements.insert(statements.begin(), func); 145 | rootCtx.registerFunc(func); 146 | 147 | // str_append(list, val) 148 | args.push_back(new NVariableDeclaration(*ct, *(new NIdentifier("val")))); 149 | func = generateFuncDecl(*(new Type(TTVOID)), "str_append", args); 150 | statements.insert(statements.begin(), func); 151 | rootCtx.registerFunc(func); 152 | 153 | // int_print(val) & int_println(val) 154 | args.clear(); 155 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("val")))); 156 | func = generateFuncDecl(*(new Type(TTVOID)), "int_print", args); 157 | statements.insert(statements.begin(), func); 158 | rootCtx.registerFunc(func); 159 | func = generateFuncDecl(*(new Type(TTVOID)), "int_println", args); 160 | statements.insert(statements.begin(), func); 161 | rootCtx.registerFunc(func); 162 | 163 | // str_insert(list, idx, val) 164 | args.clear(); 165 | args.push_back(new NVariableDeclaration(*(new Type(CHAR, true)), *(new NIdentifier("l")))); 166 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("idx")))); 167 | args.push_back(new NVariableDeclaration(*ct, *(new NIdentifier("val")))); 168 | func = generateFuncDecl(*(new Type(TTVOID)), "str_insert", args); 169 | statements.insert(statements.begin(), func); 170 | rootCtx.registerFunc(func); 171 | 172 | // str_substr(list, idx, len) 173 | args.clear(); 174 | args.push_back(new NVariableDeclaration(*(new Type(TTCHAR, true)), *(new NIdentifier("l")))); 175 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("idx")))); 176 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("len")))); 177 | func = generateFuncDecl(*(new Type(TTCHAR, true)), "str_substr", args); 178 | statements.insert(statements.begin(), func); 179 | rootCtx.registerFunc(func); 180 | 181 | // list_t * prog_argument(int) 182 | args.clear(); 183 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("idx")))); 184 | func = generateFuncDecl(*(new Type(TTCHAR, true)), "prog_argument", args); 185 | statements.insert(statements.begin(), func); 186 | rootCtx.registerFunc(func); 187 | 188 | // uint64_t prog_arg_count() 189 | args.clear(); 190 | func = generateFuncDecl(*(new Type(TTINT)), "prog_arg_count", args); 191 | statements.insert(statements.begin(), func); 192 | rootCtx.registerFunc(func); 193 | 194 | // crema_seq(start, end) 195 | args.clear(); 196 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("start")))); 197 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("end")))); 198 | func = generateFuncDecl(*(new Type(TTINT, true)), "crema_seq", args); 199 | statements.insert(statements.begin(), func); 200 | rootCtx.registerFunc(func); 201 | 202 | // ************************ Type Conversion ***************************** // 203 | 204 | // double_to_int 205 | args.clear(); 206 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 207 | func = generateFuncDecl(*(new Type(TTINT)), "double_to_int", args); 208 | statements.insert(statements.begin(), func); 209 | rootCtx.registerFunc(func); 210 | 211 | // int_to_double 212 | args.clear(); 213 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("val")))); 214 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "int_to_double", args); 215 | statements.insert(statements.begin(), func); 216 | rootCtx.registerFunc(func); 217 | 218 | // int_to_string 219 | args.clear(); 220 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("val")))); 221 | func = generateFuncDecl(*(new Type(TTCHAR, true)), "int_to_string", args); 222 | statements.insert(statements.begin(), func); 223 | rootCtx.registerFunc(func); 224 | 225 | // string_to_int 226 | args.clear(); 227 | args.push_back(new NVariableDeclaration(*(new Type(*ct, true)), *(new NIdentifier("val")))); 228 | func = generateFuncDecl(*(new Type(TTINT)), "string_to_int", args); 229 | statements.insert(statements.begin(), func); 230 | rootCtx.registerFunc(func); 231 | 232 | // string_to_double 233 | args.clear(); 234 | args.push_back(new NVariableDeclaration(*(new Type(*ct, true)), *(new NIdentifier("val")))); 235 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "string_to_double", args); 236 | statements.insert(statements.begin(), func); 237 | rootCtx.registerFunc(func); 238 | 239 | // *************************** Math Functions *************************** // 240 | 241 | // double_floor 242 | args.clear(); 243 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 244 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_floor", args); 245 | statements.insert(statements.begin(), func); 246 | rootCtx.registerFunc(func); 247 | 248 | // double_ceiling 249 | args.clear(); 250 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 251 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_ceiling", args); 252 | statements.insert(statements.begin(), func); 253 | rootCtx.registerFunc(func); 254 | 255 | // double_round 256 | args.clear(); 257 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 258 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_round", args); 259 | statements.insert(statements.begin(), func); 260 | rootCtx.registerFunc(func); 261 | 262 | // double_truncate 263 | args.clear(); 264 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 265 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_truncate", args); 266 | statements.insert(statements.begin(), func); 267 | rootCtx.registerFunc(func); 268 | 269 | // double_square 270 | args.clear(); 271 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 272 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_square", args); 273 | statements.insert(statements.begin(), func); 274 | rootCtx.registerFunc(func); 275 | 276 | // int_square 277 | args.clear(); 278 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("val")))); 279 | func = generateFuncDecl(*(new Type(TTINT)), "int_square", args); 280 | statements.insert(statements.begin(), func); 281 | rootCtx.registerFunc(func); 282 | 283 | // double_sin 284 | args.clear(); 285 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 286 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_sin", args); 287 | statements.insert(statements.begin(), func); 288 | rootCtx.registerFunc(func); 289 | 290 | // double_cos 291 | args.clear(); 292 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 293 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_cos", args); 294 | statements.insert(statements.begin(), func); 295 | rootCtx.registerFunc(func); 296 | 297 | // double_tan 298 | args.clear(); 299 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 300 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_tan", args); 301 | statements.insert(statements.begin(), func); 302 | rootCtx.registerFunc(func); 303 | 304 | // double_sqrt 305 | args.clear(); 306 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 307 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_sqrt", args); 308 | statements.insert(statements.begin(), func); 309 | rootCtx.registerFunc(func); 310 | 311 | // double_pow 312 | args.clear(); 313 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("base")))); 314 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("power")))); 315 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_pow", args); 316 | statements.insert(statements.begin(), func); 317 | rootCtx.registerFunc(func); 318 | 319 | // int_pow 320 | args.clear(); 321 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("base")))); 322 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("power")))); 323 | func = generateFuncDecl(*(new Type(TTINT)), "int_pow", args); 324 | statements.insert(statements.begin(), func); 325 | rootCtx.registerFunc(func); 326 | 327 | // double_abs 328 | args.clear(); 329 | args.push_back(new NVariableDeclaration(*(new Type(TTDOUBLE)), *(new NIdentifier("val")))); 330 | func = generateFuncDecl(*(new Type(TTDOUBLE)), "double_abs", args); 331 | statements.insert(statements.begin(), func); 332 | rootCtx.registerFunc(func); 333 | 334 | // int_abs 335 | args.clear(); 336 | args.push_back(new NVariableDeclaration(*(new Type(TTINT)), *(new NIdentifier("val")))); 337 | func = generateFuncDecl(*(new Type(TTINT)), "int_abs", args); 338 | statements.insert(statements.begin(), func); 339 | rootCtx.registerFunc(func); 340 | } 341 | 342 | /** 343 | Smart constructor for NChar to handle backslash escaped values 344 | 345 | @param str String of escaped char 346 | */ 347 | NChar::NChar(std::string str) 348 | { 349 | if (str[1] == '\\') 350 | { 351 | switch (str[2]) 352 | { 353 | case 'n': 354 | value = '\n'; 355 | break; 356 | case 't': 357 | value = '\t'; 358 | break; 359 | default: 360 | value = '\\'; 361 | break; 362 | } 363 | } else { 364 | value = str[1]; 365 | } 366 | } 367 | 368 | /** 369 | Print function for NBlock objects in the AST. Each block is composed of a vector of 370 | statements. This function iterators over the typedef std::vector StatementList, 371 | printing each each statement in a given block, and returning the output stream. 372 | 373 | @param os Output stream to print to 374 | @return Output stream passed in 375 | */ 376 | std::ostream & NBlock::print(std::ostream & os) const 377 | { 378 | os << "Block: {" << std::endl; 379 | 380 | for (StatementList::const_iterator it = statements.begin(); it != statements.end(); ++it) 381 | os << *(*it) << std::endl; 382 | 383 | os << "}" << std::endl; 384 | return os; 385 | } 386 | 387 | /** 388 | Print function for NVariableDeclaration objects. There are three members of the 389 | NVariableDeclaration object that are printed: 390 | 391 | 1) Type 392 | 2) NIdentifier (Variable name) 393 | 3) Associated value or RHS (optional) 394 | 395 | The initializationExpression is the RHS in variable assignments (e.g. the 4 in int a = 4). 396 | If initializationExpression is NULL, then the no value was assigned to variable (e.g. int a). 397 | 398 | @param os Output stream to print to 399 | @return Output stream passed in 400 | */ 401 | std::ostream & NVariableDeclaration::print(std::ostream & os) const 402 | { 403 | if (!type.isList) 404 | { 405 | os << "Variable declared --- (" << type << " " << ident << ")"; 406 | } 407 | else 408 | { 409 | os << "List declared --- (" << type << " " << ident << "[])"; 410 | } 411 | if (initializationExpression != NULL) 412 | { 413 | os << " = " << *initializationExpression; 414 | } 415 | os << std::endl; 416 | return os; 417 | } 418 | 419 | /** 420 | Print function for NFunctionDeclaration objects. It first prints the return type 421 | and name of the function followed by the arguments contained within the 422 | typedef std::vector VariableList. Then, the NBlock body 423 | of the function is printed. 424 | 425 | @param os Output stream to print to 426 | @return Output stream passed in 427 | */ 428 | std::ostream & NFunctionDeclaration::print(std::ostream & os) const 429 | { 430 | os << "Function declared --- (" << type << " " << ident << "("; 431 | 432 | for (VariableList::const_iterator it = variables.begin(); it != variables.end(); ++it) 433 | os << *(*it) << ") "; 434 | 435 | if (body) 436 | { 437 | os << *body << ")"; 438 | os << std::endl; 439 | } 440 | return os; 441 | } 442 | 443 | /** 444 | Print function for NStructureDeclaration objects. It first prints the name of the 445 | struct followed by its typedef std::vector VariableList 446 | members. 447 | 448 | @params os Output stream to print to 449 | @return Output stream passed on 450 | */ 451 | std::ostream & NStructureDeclaration::print(std::ostream & os) const 452 | { 453 | os << "Struct declared --- (" << ident << " {"; 454 | 455 | for (VariableList::const_iterator it = members.begin(); it != members.end(); ++it) 456 | os << *(*it) << " "; 457 | 458 | os << "})"; 459 | os << std::endl; 460 | 461 | return os; 462 | } 463 | 464 | /** 465 | Print function for NAssignmentStatement objects, which are inherited from the 466 | NStatement class. Similar to the NVariableDeclaration, this object is used 467 | after a variable has been declared, but is subsequently given a value in 468 | another statement. For example, the second statement in, 469 | int a 470 | a = 4 471 | is the assignment statement. 472 | 473 | @param os Output stream to print to 474 | @return Output stream passed on 475 | */ 476 | std::ostream & NAssignmentStatement::print(std::ostream & os) const 477 | { 478 | os << "(Assignment: " << ident << " = " << expr << ")" << std::endl; 479 | return os; 480 | } 481 | 482 | /** 483 | Print function for NListAssignmentStatement objects, which are inherited from the 484 | NAssignmentStatement class. The NListAccess &list variable that is printed contains the 485 | name (NIdentifier &ident) and the index (NExpression &index) of each element in 486 | the list. The expr value is the RHS of the expression in the assignment statement. 487 | (Not to be confused with a declaration statement.) 488 | 489 | @param os Output stream to print to 490 | @return Output stream passed on 491 | */ 492 | std::ostream & NListAssignmentStatement::print(std::ostream & os) const 493 | { 494 | os << "(Assignment: " << list << " = " << expr << "<" << std::endl; 495 | return os; 496 | } 497 | 498 | /** 499 | Print function for NStructureAssignmentStatement objects, which are inherited from 500 | the NAssignmentStatement class. The NStructure &structure variable that is printed 501 | contains the structure name (NIdentifier &ident) and its members (NIdentifier &member). 502 | The expr value is the RHS of the expression in the assignment statement. (Not to be 503 | confused with a declaration statement.) 504 | 505 | @param os Output stream to print to 506 | @return Output stream passed on 507 | */ 508 | std::ostream & NStructureAssignmentStatement::print(std::ostream & os) const 509 | { 510 | os << "(Assignment: " << structure << " = " << expr << ")" << std::endl; 511 | return os; 512 | } 513 | 514 | /** 515 | Print function for NBinaryOperator objects, which are inherited from the NExpression 516 | class. This function prints the LHS, the binary operator symbol, followed by the RHS 517 | of the expression. 518 | 519 | @param os Output stream to print to 520 | @return Output stream passed on 521 | */ 522 | std::ostream & NBinaryOperator::print(std::ostream & os) const 523 | { 524 | std::string symbol; 525 | switch(op) 526 | { 527 | case TMUL: 528 | symbol = "*"; 529 | break; 530 | case TADD: 531 | symbol = "+"; 532 | break; 533 | case TDIV: 534 | symbol = "/"; 535 | break; 536 | case TSUB: 537 | symbol = "-"; 538 | break; 539 | case TMOD: 540 | symbol = "%"; 541 | break; 542 | case TBAND: 543 | symbol = "&"; 544 | break; 545 | case TBXOR: 546 | symbol = "^"; 547 | break; 548 | case TBOR: 549 | symbol = "|"; 550 | break; 551 | case TLNOT: 552 | symbol = "!"; 553 | break; 554 | case TLOR: 555 | symbol = "||"; 556 | break; 557 | case TLAND: 558 | symbol = "&&"; 559 | break; 560 | case TCEQ: 561 | symbol = "=="; 562 | break; 563 | case TCNEQ: 564 | symbol = "!="; 565 | break; 566 | case TCGT: 567 | symbol = ">"; 568 | break; 569 | case TCLT: 570 | symbol = "<"; 571 | break; 572 | case TCGE: 573 | symbol = ">="; 574 | break; 575 | case TCLE: 576 | symbol = "<="; 577 | break; 578 | default: 579 | symbol = "UNKNOWN OP"; 580 | break; 581 | 582 | } 583 | os << "(BINOP: " << lhs << " " << symbol << " " << rhs << ")" << std::endl; 584 | return os; 585 | } 586 | 587 | /** 588 | Prints the name of a given NIdentifier object. (Note: the variable 'value' 589 | is a string.) 590 | 591 | @param os Output stream to print to 592 | @return Output stream passed on 593 | */ 594 | std::ostream & NIdentifier::print(std::ostream & os) const 595 | { 596 | os << value; 597 | return os; 598 | } 599 | 600 | /** 601 | Prints an NListAccess object, which is a list element containing an NIdentifier &ident 602 | and an NExpression &index. (Note: an index may be more than just an integer, like a[1+2] 603 | would be the third element in a list.) 604 | 605 | @param os Output stream to print to 606 | @return Output stream passed on 607 | */ 608 | std::ostream & NListAccess::print(std::ostream & os) const 609 | { 610 | os << "(List access: " << ident << "[" << *index << "])"; 611 | return os; 612 | } 613 | 614 | /** 615 | Prints an NVariableAccess object, which is inherited from NExpression and contains an 616 | NIdentifier &ident member. The ident variable is the name of the variable being accessed. 617 | 618 | @param os Output stream to print to 619 | @return Output stream passed on 620 | */ 621 | std::ostream & NVariableAccess::print(std::ostream & os) const 622 | { 623 | os << "(Variable access: " << ident << ")"; 624 | return os; 625 | } 626 | 627 | /** 628 | Prints an NStructureAccess object, which is inherited from the NExpression class and contains both 629 | an NIdentifier &ident member (the struct name) and an NIdentifier &member member (the 630 | struct's member name being accessed). 631 | 632 | @param os Output stream to print to 633 | @return Output stream passed on 634 | */ 635 | std::ostream & NStructureAccess::print(std::ostream & os) const 636 | { 637 | os << "(Struct access: " << ident << "." << member << ")"; 638 | return os; 639 | } 640 | 641 | /** 642 | Prints an NReturn object, which is inherited from the NStatement class. The retExpr variable 643 | is of type NExpression. 644 | 645 | @param os Output stream to print to 646 | @return Output stream passed on 647 | */ 648 | std::ostream & NReturn::print(std::ostream & os) const 649 | { 650 | os << "(Return: " << retExpr << ")"; 651 | os << std::endl; 652 | return os; 653 | } 654 | 655 | /** 656 | Prints an NBreak object, which is inherited from the NStatement class. 657 | 658 | @param os Output stream to print to 659 | @return Output stream passed on 660 | */ 661 | std::ostream & NBreak::print(std::ostream & os) const 662 | { 663 | os << "(Break)"; 664 | os << std::endl; 665 | return os; 666 | } 667 | 668 | /** 669 | Prints an NDouble object, which is inherited from the NValue class. The variable 'value' 670 | that is printed is the number itself, not the string name of the identifier. 671 | 672 | @param os Output stream to print to 673 | @return Output stream passed on 674 | */ 675 | std::ostream & NDouble::print(std::ostream & os) const 676 | { 677 | os << "DOUBLE:" << value; 678 | return os; 679 | } 680 | 681 | /** 682 | Prints an NBool object, which is inherited from the NValue class. The variable 'value' 683 | that is printed is 'true (1)' or 'false (0)', not the string name of the identifier. 684 | 685 | @param os Output stream to print to 686 | @return Output stream passed on 687 | */ 688 | std::ostream & NBool::print(std::ostream & os) const 689 | { 690 | os << "BOOL: " << (value ? "true" : "false"); 691 | return os; 692 | } 693 | 694 | /** 695 | Prints an NInt object, which is inherited from the NValue class. The variable 'value' 696 | that is printed is the number itself, not the string name of the identifier. 697 | 698 | @param os Output stream to print to 699 | @return Output stream passed on 700 | */ 701 | std::ostream & NInt::print(std::ostream & os) const 702 | { 703 | os << "INT:" << value; 704 | return os; 705 | } 706 | 707 | /** 708 | Prints an NChar object, which is inherited from the NValue class. The variable 'value' 709 | that is printed is the number itself, not the string name of the identifier. 710 | 711 | @param os Output stream to print to 712 | @return Output stream passed on 713 | */ 714 | std::ostream & NChar::print(std::ostream & os) const 715 | { 716 | os << "CHAR:" << value; 717 | return os; 718 | } 719 | 720 | /** 721 | Print function that allows for the genertic printing of an NValue object. The NValue 722 | class does not contain any variables, thus there are not any variables printed to the 723 | output stream in this function. 724 | 725 | @param os Output stream to print to 726 | @return Output stream passed on 727 | */ 728 | std::ostream & NValue::print(std::ostream & os) const 729 | { 730 | os << "(Generic NValue)" << std::endl; 731 | return os; 732 | } 733 | 734 | /** 735 | Prints an NUInt object, which is inherited from the NValue class. The variable 'value' 736 | that is printed is the number itself, not the string name of the identifier. 737 | 738 | @param os Output stream to print to 739 | @return Output stream passed on 740 | */ 741 | std::ostream & NUInt::print(std::ostream & os) const 742 | { 743 | os << "UINT:" << value; 744 | return os; 745 | } 746 | 747 | /** 748 | Prints an NString object, which is inherited from the NValue class. The variable 'value' 749 | that is printed is the string itself, not the string name of the identifier. 750 | 751 | @param os Output stream to print to 752 | @return Output stream passed on 753 | */ 754 | std::ostream & NString::print(std::ostream & os) const 755 | { 756 | os << "STRING:" << value; 757 | return os; 758 | } 759 | 760 | /** 761 | Prints NLoopStatement objects, which are inherited from the NStatement class. The variable 762 | NIdentifer &list is the list variable name that is being looped through. NIdentifier &asVar 763 | is the temporary name inside of the loop block to reference the current list element. And 764 | the NBlock &loopBlock are the statements contained with in the braces of the loop statement. 765 | 766 | @param os Output stream to print to 767 | @return Output stream passed on 768 | */ 769 | std::ostream & NLoopStatement::print(std::ostream & os) const 770 | { 771 | os << "Loop: " << list << " as " << asVar << std::endl; 772 | os << "{" << loopBlock << "}" << std::endl; 773 | return os; 774 | } 775 | 776 | /** 777 | Prints NIfStatement objects, which are inherited from the NStatement class. There are four 778 | possible members that may be printed: 779 | 1) NExpression &condition, the 'if' condition in parentheses 780 | 2) NBlock &thenblock, the series of statements that follow the 'if' condition 781 | 3) NBlock *elseblock, a pointer to a statement or series of statements for 'else' conditions 782 | 4) NStatement *elseif, a pointer to one statement completing the 'if' statement 783 | 784 | @param os Output stream to print to 785 | @return Output stream passed on 786 | */ 787 | std::ostream & NIfStatement::print(std::ostream & os) const 788 | { 789 | if (elseblock == NULL && elseif == NULL) 790 | os << "If: (" << condition << ") then " << thenblock << std::endl; 791 | if (elseblock != NULL && elseif == NULL) 792 | os << "If: (" << condition << ") then: " << thenblock << " else: " << *(elseblock) << std::endl; 793 | if (elseblock == NULL && elseif != NULL) 794 | { 795 | os << "If: (" << condition << ") then " << thenblock << std::endl; 796 | os << "Else if: " << *(elseif) << std::endl; 797 | } 798 | if (elseblock != NULL && elseif != NULL) 799 | { 800 | os << "If: (" << condition << ") then " << thenblock << std::endl; 801 | os << "Else if: " << *(elseif) << " else: " << *(elseblock) << std::endl; 802 | } 803 | return os; 804 | } 805 | 806 | /** 807 | Prints FunctionCall objects, which are inherited from the NExpression class. The 'args' 808 | variable is a typedef std::vector ExpressionList and contains the arguments 809 | passed to the function. 810 | 811 | @param os Output stream to print to 812 | @return Output stream passed on 813 | */ 814 | std::ostream & NFunctionCall::print(std::ostream & os) const 815 | { 816 | os << "Function Call: " << ident << std::endl; 817 | 818 | for(ExpressionList::const_iterator it = args.begin(); it != args.end(); ++it) 819 | os << *(*it) << std::endl; 820 | 821 | return os; 822 | } 823 | 824 | /** 825 | Prints NList objects, which are inherited from the NValue class. The 'value' variable 826 | is a typedef std::vector ExpressionList and contains the values of the 827 | list items, not the names of the items. 828 | 829 | @param os Output stream to print to 830 | @return Output stream passed on 831 | */ 832 | std::ostream & NList::print(std::ostream & os) const 833 | { 834 | os << "List: ["; 835 | 836 | for (ExpressionList::const_iterator it = value.begin(); it != value.end(); ++it) 837 | os << *(*it) << " "; 838 | 839 | os << "]" << std::endl; 840 | return os; 841 | } 842 | -------------------------------------------------------------------------------- /src/semantics.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file semantics.cpp 3 | @brief Implementation for semantic analysis-related functionality of cremacc checking 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | This file contains the implementations for all functionality related to semantic analysis. 8 | This includes the SemanticContext implementation and the typing functions for the AST functions. 9 | */ 10 | #include "semantics.h" 11 | #include "ast.h" 12 | #include "parser.h" 13 | #include "types.h" 14 | #include 15 | 16 | /** 17 | * The root SemanticContext object to use when performing semantic analysis */ 18 | SemanticContext rootCtx; 19 | 20 | SemanticContext::SemanticContext() 21 | { 22 | Type t; 23 | t.isList = false; 24 | t.typecode = INT; 25 | newScope(t); 26 | currScope = 0; 27 | inList = false; 28 | inFunc = false; 29 | } 30 | 31 | /** 32 | Creates a new scope for variable declarations. 33 | 'vars' is a std::vector and VariableList is a 34 | typedef std::vector. So when newScope is called, 35 | an empty vector of NVariableDeclarations is pushed to the back of the 36 | vars vector. 'currType' is a std::vector. The new type being pushed 37 | to the back of currType contains a boolean 'isList' member and a 38 | TypeCodes 'typecode' variable, which is an enum containing all the possible 39 | types. currScope is incremented by one. 40 | 41 | @param type The return type of the scope (0 if void) 42 | */ 43 | void SemanticContext::newScope(Type & type) 44 | { 45 | vars.push_back(new VariableList()); 46 | currType.push_back(type); 47 | funcReturns.push_back(false); 48 | currScope++; 49 | } 50 | 51 | /** 52 | Deletes the most recent scope. 53 | Pops back the most recent VariableList (typedef std::vector) 54 | from the vars vector and the most recent Type object from the currType vector. 55 | currScope is decremented by one. 56 | */ 57 | void SemanticContext::delScope() 58 | { 59 | vars.pop_back(); 60 | currType.pop_back(); 61 | currScope--; 62 | } 63 | 64 | /** 65 | Registers the variable into the current scope and returns true or false depending 66 | on whether it was successfully added. The function looks to see if there is an 67 | existing variable name (ident) in the current scope by iterating through the 68 | VariableList (typedef std::vector) and comparing the 69 | argument member, var->ident, with the other ident members within the VariableList. 70 | If there is no duplicate, the function will push_back the var class object to the 71 | vars vector. 72 | 73 | @param var Pointer to the NVariableDeclaration to add to the current scope 74 | @return true if the variable was added, false if it is a duplicate 75 | */ 76 | bool SemanticContext::registerVar(NVariableDeclaration * var) 77 | { 78 | if (NULL != searchFuncs(var->ident)) 79 | return false; 80 | 81 | // Search through current scope for variable duplication 82 | for (auto it : (*(vars[currScope]))) 83 | if ( var->ident == it->ident ) 84 | return false; 85 | 86 | vars[currScope]->push_back(var); 87 | return true; 88 | } 89 | 90 | /** 91 | Registers the function into the global scope and returns true or false depending 92 | on whether it was successfully added. The function searches for the function name 93 | being passed as an argument (func->ident) matches with exising functions in 94 | the funcs vector. 95 | 96 | @param func Pointer to the NFunctionDeclaration to add to the global scope 97 | @return true if the function was added, false if it is a duplicate 98 | */ 99 | bool SemanticContext::registerFunc(NFunctionDeclaration * func) 100 | { 101 | if (NULL != searchVars(func->ident)) 102 | return false; 103 | 104 | // Search through for duplicate function duplication 105 | for (auto it : funcs) 106 | if ( func->ident == it->ident ) 107 | return false; 108 | 109 | funcs.push_back(func); 110 | return true; 111 | } 112 | 113 | /** 114 | Registers the structure into the global scope and returns true or false depending 115 | on whether it was successfully added. The function searches for struct name 116 | being passed as an argument (s->ident) matches with existing struct names 117 | in the structs vector. 118 | 119 | @param s Pointer to the NStructureDeclaration to add to the global scope 120 | @return true if the structure was added, false if it is a duplicate 121 | */ 122 | bool SemanticContext::registerStruct(NStructureDeclaration * s) 123 | { 124 | // Search through for duplicate struct duplication 125 | for (auto it : structs) 126 | if ( s->ident == it->ident ) 127 | return false; 128 | 129 | structs.push_back(s); 130 | return true; 131 | } 132 | 133 | /** 134 | Searches the local, then parent scopes for a variable declaration. The search begins 135 | at the back of the std::vector vars vector. At each vars element the inner 136 | for loop looks for matches between the function argument ident and the ident member of the 137 | NVariableDeclaration class. The function then returns a pointer to the class object of the 138 | referenced variable. 139 | 140 | @param ident NIdentifier to search for in the stack of scopes 141 | @return Pointer to NVariableDeclaration of the referenced variable or NULL if it cannot be found 142 | */ 143 | NVariableDeclaration * SemanticContext::searchVars(NIdentifier & ident) 144 | { 145 | // Search through stacks in reverse order 146 | for (int i = vars.size()-1; i >= 0; i--) 147 | { 148 | // Search through current scope for variable 149 | for (int j = 0; j < (vars[i])->size(); j++) 150 | { 151 | if (ident == vars[i]->at(j)->ident) 152 | return vars[i]->at(j); 153 | } 154 | } 155 | 156 | return NULL; 157 | } 158 | 159 | /** 160 | Searches for a function declaration. A std::vector::iterator iterates over the funcs 161 | vector and the if statement compares the name of the argument ident with each function name 162 | funcs[index]->ident. 163 | 164 | @param ident NIdentifier to search for in the global function scope 165 | @return Pointer to NFunctionDeclaration of the referenced function or NULL if it cannot be found 166 | */ 167 | NFunctionDeclaration * SemanticContext::searchFuncs(NIdentifier & ident) 168 | { 169 | for (auto it : funcs) 170 | if (ident == it->ident) 171 | return it; 172 | 173 | return NULL; 174 | } 175 | 176 | 177 | /** 178 | Searches for a structure declaration. A std::vector::iterator moves over the structs 179 | vector and the if statement compares the name of the argument ident with each struct name 180 | struct[index]->ident. 181 | 182 | @param ident NIdentifier to search for in the global structure scope 183 | @return Pointer to NStructureDeclaration of the referenced structure or NULL if it cannot be found 184 | */ 185 | NStructureDeclaration * SemanticContext::searchStructs(NIdentifier & ident) 186 | { 187 | for (auto it : structs) 188 | if (ident == it->ident) 189 | return it; 190 | 191 | return NULL; 192 | } 193 | 194 | /** 195 | Iterates over the vector and returns 'false' if any of 196 | semanticAnalysis elements are false. 197 | 198 | @param ctx Pointer to the SemanticContext on which to perform the checks 199 | @return true if the block passes semantic analysis, false otherwise 200 | */ 201 | bool NBlock::semanticAnalysis(SemanticContext * ctx) 202 | { 203 | // Points to the last element in the vector currType. 204 | ctx->newScope(ctx->currType.back()); 205 | 206 | for( auto it : statements ) 207 | if (!((*it).semanticAnalysis(ctx))) 208 | return false; 209 | 210 | ctx->delScope(); 211 | return true; 212 | } 213 | 214 | /** 215 | Iterates over the vector and returns 'true' if any of 216 | checkRecursion elements are true. 217 | 218 | @param ctx Pointer to SemanticContext on which to perform the checks 219 | @param func Pointer to NFunctionDeclaration of the parent function that is being checked 220 | @return true if there is a recursive call, false otherwise 221 | */ 222 | bool NBlock::checkRecursion(SemanticContext *ctx, NFunctionDeclaration * func) 223 | { 224 | for (auto it : statements) 225 | if ((*it).checkRecursion(ctx, func)) 226 | return true; 227 | return false; 228 | } 229 | 230 | /** 231 | Checks the name of the function being called (ident) with the name of the member 232 | within the NFunctionCall object (func->ident), and returns true if the names match. 233 | Otherwise, other function names are searched within the body and checkRecursion is 234 | called again recursively. 235 | 236 | @param ctx Pointer to the SemanticContext on which to perform the checks 237 | @param func Pointer to the NFunctionDeclaration that is being checked 238 | @return true if there is a recursive call, false otherwise 239 | */ 240 | bool NFunctionCall::checkRecursion(SemanticContext * ctx, NFunctionDeclaration * func) 241 | { 242 | if (func->ident == ident) 243 | return true; 244 | if (ctx->searchFuncs(ident)->body) 245 | { 246 | return ctx->searchFuncs(ident)->body->checkRecursion(ctx, func); 247 | } 248 | return false; 249 | } 250 | 251 | /** 252 | Performs the semantic analysis of a binary operator expression. This function compares 253 | the two types of the left- and right-hand-side of the expression by calling the function 254 | getType(ctx). 255 | 256 | @param ctx Pointer to the SemanticContext on which to perform the checks 257 | @return true if lhs and rhs types mismatch, false if there is a type mismatch 258 | */ 259 | bool NBinaryOperator::semanticAnalysis(SemanticContext * ctx) 260 | { 261 | Type & t1 = lhs.getType(ctx), & t2 = rhs.getType(ctx); 262 | if (!(t1 >= t2 || t2 >= t1)) 263 | { 264 | std::cout << "Binary operator type mismatch for op: " << op << std::endl; 265 | return false; 266 | } 267 | type = Type::getLargerType(t1, t2); 268 | return true; 269 | } 270 | 271 | /** 272 | This function creates two Type objects from the lhs and the rhs of a comparison 273 | statement. If the expression is not a comparison, the 'upcast' label compares the 274 | types of the lhs and rhs and returns the type object with the higher precedence. 275 | 276 | @param ctx Pointer to the SemanticContext on which to perform the checks 277 | @return Type object based on the types of the lhs and rhs sides of the expression. 278 | */ 279 | Type & NBinaryOperator::getType(SemanticContext * ctx) const 280 | { 281 | Type & t1 = lhs.getType(ctx), & t2 = rhs.getType(ctx); 282 | 283 | switch (op) 284 | { 285 | // Comparisons return a BOOL 286 | case TCEQ: 287 | case TCNEQ: 288 | case TCLE: 289 | case TCLT: 290 | case TCGE: 291 | case TCGT: 292 | case TLOR: 293 | case TLAND: 294 | type = *(new Type(TTBOOL)); 295 | return type; 296 | break; 297 | // Mathematical binary operations return the greater of the two types as long as they can be combined 298 | default: 299 | break; 300 | } 301 | 302 | if (!(t1 >= t2 || t2 >= t1)) 303 | { 304 | return *(new Type()); 305 | } 306 | 307 | 308 | if (t1 == t2) 309 | { 310 | type = t1; 311 | return t1; 312 | } 313 | 314 | type = (t1 > t2) ? t1 : t2; 315 | return type; 316 | } 317 | 318 | /** 319 | An NVariableDeclaration pointer, var, is created and assiged to 320 | the NVariableDeclaration object result of the searchVars function, 321 | which searches for the variable name (ident) within the context, ctx. 322 | If neither the variable name is not found or there is a type mismatch, 323 | semanticAnalysis returns false. True, otherwise. 324 | 325 | @param ctx Pointer to the SemanticContext on which to perform the checks 326 | @return true if the variable and correct type are found, false otherwise. 327 | */ 328 | bool NAssignmentStatement::semanticAnalysis(SemanticContext * ctx) 329 | { 330 | NVariableDeclaration *var = ctx->searchVars(ident); 331 | if (!var) 332 | { 333 | std::cout << "Assignment to undefined variable " << ident << std::endl; 334 | return false; 335 | } 336 | Type & t = expr.getType(ctx); 337 | if (var->type < t) 338 | { 339 | std::cout << "Type mismatch (" << var->type << " vs. " << expr.getType(ctx) << ") for assignment to " << ident << std::endl; 340 | return false; 341 | } 342 | if (var->type != t) 343 | { 344 | std::cout << "Warning: Upcast from " << var->type << " to " << expr.getType(ctx) << std::endl; 345 | } 346 | return true; 347 | } 348 | 349 | /** 350 | An NVariableDeclaration pointer, var, is created and assigned to 351 | the NVariableDeclaration object result of the searchVars function, 352 | which searches for the variable name (ident) within the context, ctx. 353 | If the assignment is to an undefined variable, the struct variable 354 | already exists, or if there is a type mismatch, semanticAnalysis will 355 | return false. Otherwise, true. A warning will occur if the struture 356 | is upcast. 357 | 358 | @param ctx Pointer to the SemanticContext on which to perform the checks 359 | @return true if the assignment passes semantic analysis, and false otherwise. 360 | */ 361 | bool NStructureAssignmentStatement::semanticAnalysis(SemanticContext * ctx) 362 | { 363 | NVariableDeclaration *var = ctx->searchVars(ident); 364 | if (!var) 365 | { 366 | std::cout << "Assignment to undefined variable " << ident << std::endl; 367 | return false; 368 | } 369 | if (!var->type.isStruct) 370 | { 371 | return false; 372 | } 373 | if (structure.getType(ctx) < expr.getType(ctx)) 374 | { 375 | std::cout << "Type mismatch (" << structure.getType(ctx) << " vs. " << expr.getType(ctx) << ") for assignment to " << ident << std::endl; 376 | return false; 377 | } 378 | if (structure.getType(ctx) != expr.getType(ctx)) 379 | { 380 | std::cout << "Warning: Upcast from " << structure.getType(ctx) << " to " << expr.getType(ctx) << std::endl; 381 | } 382 | return true; 383 | } 384 | 385 | /** 386 | An NVariableDeclaration pointer, var, is created and assigned to the 387 | NVariableDeclaration object result of the searchVars funtion which 388 | searches for the variable name (ident) within the context, ctx. 389 | If the list being assigned contains and invalid name or invalid 390 | mismathed types, the semantic analysis will fail. A warning will be 391 | given for upcast events related to the typing. 392 | 393 | @param ctx Pointer to the SemanticContext on which to perform the checks 394 | @return true if list assignment passes semantic analaysis, and false otherwise. 395 | */ 396 | bool NListAssignmentStatement::semanticAnalysis(SemanticContext * ctx) 397 | { 398 | NVariableDeclaration *var = ctx->searchVars(ident); 399 | list.getType(ctx); 400 | if (!var) 401 | { 402 | std::cout << "Assignment to undefined variable " << ident << std::endl; 403 | return false; 404 | } 405 | if (list.index) 406 | { 407 | Type & it = list.index->getType(ctx); 408 | if (it.typecode != INT && it.typecode != UINT) 409 | { 410 | std::cout << "Invalid non-integer index to accessing " << ident << std::endl; 411 | return false; 412 | } 413 | } 414 | Type *t = new Type(var->type, false); 415 | if (*t < expr.getType(ctx)) 416 | { 417 | std::cout << "Type mismatch (" << *t << " vs. " << expr.getType(ctx) << ") for assignment to " << ident << std::endl; 418 | return false; 419 | } 420 | if (*t != expr.getType(ctx)) 421 | { 422 | std::cout << "Warning: Upcast from " << *t << " to " << expr.getType(ctx) << std::endl; 423 | } 424 | return true; 425 | } 426 | 427 | /** 428 | If the return expression type is further up the stack than the current 429 | context type, this function will return false. True, otherwise. 430 | 431 | @param ctx Pointer to the SemanticContext on which to perform the checks 432 | @return true if the type of the returned expression agrees with the current 433 | context at the back of the stack. 434 | */ 435 | bool NReturn::semanticAnalysis(SemanticContext * ctx) 436 | { 437 | if (retExpr.getType(ctx) > ctx->currType.back()) 438 | { 439 | std::cout << "Returning type " << retExpr.getType(ctx) << " when a " << ctx->currType.back() << " was expected" << std::endl; 440 | return false; 441 | } 442 | if (retExpr.getType(ctx) != ctx->currType.back()) 443 | { 444 | std::cout << "Warning: Upcast from " << retExpr.getType(ctx) << " to " << ctx->currType.back() << std::endl; 445 | } 446 | ctx->funcReturns.back() = true; 447 | return true; 448 | } 449 | 450 | /** 451 | This functions gets a type object from the semantic context of a list. 452 | First, a reference to a type object is created and either the Type() 453 | constructor is called (if value.size() !> 0) or the reference is to 454 | the first index of the value vector. A pointer to this is then created. 455 | Then, the type is compared to the other types in the list and if a 456 | different type is encoutered, a pointer to a new Type object is returned. 457 | Otherwise, the function returns the pointer *lt. 458 | 459 | @param ctx Pointer to the SemanticContext on which to perform the checks 460 | @return pointer to a Type object. 461 | */ 462 | Type & NList::getType(SemanticContext * ctx) const 463 | { 464 | Type & intype = (value.size() > 0) ? value[0]->getType(ctx) : *(new Type()); 465 | Type *lt = new Type(intype, true); 466 | for (int i = 1; i < value.size(); i++) 467 | { 468 | if (value[i]->getType(ctx) != intype) 469 | return *(new Type()); 470 | } 471 | type = *lt; 472 | return *lt; 473 | } 474 | 475 | /** 476 | This function checks whether the types within a list are the same. 477 | 478 | @param ctx Pointer to the SemanticContext on which to perform the checks 479 | @return true if all types within a list match and false otherwise. 480 | */ 481 | bool NList::semanticAnalysis(SemanticContext * ctx) 482 | { 483 | Type & intype = (value.size() > 0) ? value[0]->getType(ctx) : *(new Type()); 484 | for (int i = 1; i < value.size(); i++) 485 | { 486 | if (value[i]->getType(ctx) != intype) 487 | { 488 | std::cout << "List element: " << i << " contains differing types: " << value[i]->getType(ctx) << " vs. " << intype << std::endl; 489 | return false; 490 | } 491 | } 492 | 493 | return true; 494 | } 495 | 496 | /** 497 | Gets the type of a variable or creates a new Type() object if the 498 | searchVars(ident) function does not find the name of an existing 499 | variable. 500 | 501 | @param ctx Pointer to the SemanticContext on which to perform the checks 502 | @return Type object of the variable or a new Type object if it doesn't exist 503 | */ 504 | Type & NVariableAccess::getType(SemanticContext * ctx) const 505 | { 506 | NVariableDeclaration *var = ctx->searchVars(ident); 507 | if (var) 508 | { 509 | type = var->type; 510 | return var->type; 511 | } 512 | return *(new Type()); 513 | } 514 | 515 | /** 516 | Checks whether the variable in the current semantic context exists, and 517 | returns the boolean result. 518 | 519 | @param ctx Pointer to the SemanticContext on which to perform the checks 520 | @return true if the variable name is found in searchVars(ident) function, false otherwise 521 | */ 522 | bool NVariableAccess::semanticAnalysis(SemanticContext * ctx) 523 | { 524 | NVariableDeclaration *var = ctx->searchVars(ident); 525 | if (var) 526 | { 527 | return true; 528 | } 529 | return false; 530 | } 531 | 532 | /** 533 | Checks whether a list a present in the current semantic context. 534 | 535 | @param ctx Pointer to the SemanticContext on which to perform the checks 536 | @return true if found, false if not found or if variable is not a list. 537 | */ 538 | bool NListAccess::semanticAnalysis(SemanticContext * ctx) 539 | { 540 | NVariableDeclaration *var = ctx->searchVars(ident); 541 | if (var) 542 | { 543 | if (!var->type.isList) 544 | { 545 | return false; 546 | } 547 | Type & t = index->getType(ctx); 548 | if (t.typecode != INT && t.typecode != UINT) 549 | { 550 | return false; 551 | } 552 | return true; 553 | } 554 | return false; 555 | 556 | } 557 | 558 | /** 559 | Accesses a list element and returns the Type object of the element's type. 560 | 561 | @param ctx Pointer to the SemanticContext on which to perform the checks 562 | @return A pointer to the list element's type or a pointer to a new Type object. 563 | */ 564 | Type & NListAccess::getType(SemanticContext * ctx) const 565 | { 566 | NVariableDeclaration *var = ctx->searchVars(ident); 567 | if (var) 568 | { 569 | Type *st = new Type(var->type, false); 570 | index->getType(ctx); 571 | type = *st; 572 | return *st; 573 | } 574 | return *(new Type()); 575 | } 576 | 577 | /** 578 | Gets the Type object of an NFunctionCall object. If the context exists within 579 | the searchFuncs(ident), then a pointer to that type is returned. Otherwise, 580 | a new Type object is created and returned. 581 | 582 | @param ctx Pointer to the SemanticContext on which to perform the checks 583 | @return Type object of the function call or new Type object is it doesn't already exist. 584 | */ 585 | Type & NFunctionCall::getType(SemanticContext * ctx) const 586 | { 587 | NFunctionDeclaration *func = ctx->searchFuncs(ident); 588 | for (int i = 0; i < args.size(); i++) 589 | { 590 | args[i]->getType(ctx); 591 | } 592 | if (func) 593 | { 594 | type = func->type; 595 | return func->type; 596 | } 597 | return *(new Type()); 598 | } 599 | 600 | /** 601 | Does a semantic check for a function call and returns a boolean value. If there 602 | is an invalid number of arguments or a type mismatch, the function returns false. 603 | True otherwise. A warning is given in the case of type upcasting. 604 | 605 | @param ctx Pointer to the SemanticContext on which to perform the checks 606 | @return true if the number of args and types agree, false otherwise 607 | */ 608 | bool NFunctionCall::semanticAnalysis(SemanticContext * ctx) 609 | { 610 | NFunctionDeclaration *func = ctx->searchFuncs(ident); 611 | if (func) 612 | { 613 | if (func->variables.size() != args.size()) 614 | { 615 | std::cout << "Call to " << ident << " with invalid number of arguments! " << func->variables.size() << " expected, " << args.size() << " provided" << std::endl; 616 | return false; 617 | } 618 | for (int i = 0; i < args.size(); i++) 619 | { 620 | if (args[i]->getType(ctx) > func->variables[i]->type) 621 | { 622 | std::cout << "Type mismatch when calling function: " << ident << " on line " << lineno << std::endl; 623 | return false; 624 | } 625 | if (args[i]->getType(ctx) != func->variables[i]->type) 626 | { 627 | std::cout << "Warning: Type upcast for argument " << i+1 << " '" << func->variables[i]->ident << "' " << "of function call: '" << ident << "' on line " << lineno << std::endl; 628 | } 629 | } 630 | return true; 631 | } 632 | std::cout << "Call to undefined function: " << ident << std::endl; 633 | return false; 634 | } 635 | 636 | /** 637 | Performs a semantic check for a loop statement. If the list variable is not defined 638 | or the variable is not a list, the function returns false. Otherwise, semantic 639 | analysis is perform on the loopBlock, delScope() method is called and the result 640 | of the semantic analysis on the block is returned. 641 | 642 | @param ctx Pointer to the SemanticContext on which to perform checks. 643 | @return true if the semantic analysis on the block returns true, false if undefined or not a list. 644 | */ 645 | bool NLoopStatement::semanticAnalysis(SemanticContext * ctx) 646 | { 647 | NVariableDeclaration *l = ctx->searchVars(list); 648 | bool blockSA, oldList; 649 | Type *st; 650 | 651 | if (NULL == l) 652 | { 653 | std::cout << "List variable " << list << " not defined!" << std::endl; 654 | return false; 655 | } 656 | if (!l->type.isList) 657 | { 658 | std::cout << "Variable " << list << " not a list!" << std::endl; 659 | return false; 660 | } 661 | ctx->newScope(ctx->currType.back()); 662 | st = new Type(l->type, false); 663 | 664 | ctx->registerVar(new NVariableDeclaration(*st, asVar)); 665 | 666 | oldList = ctx->inList; 667 | ctx->inList = true; 668 | blockSA = loopBlock.semanticAnalysis(ctx); 669 | ctx->inList = oldList; 670 | ctx->delScope(); 671 | return blockSA; 672 | } 673 | 674 | /** 675 | Checks a break statement to ensure it is in a looping construct 676 | 677 | @param ctx Pointer to context object 678 | @return true is break is in a loop, false otherwise 679 | */ 680 | bool NBreak::semanticAnalysis(SemanticContext * ctx) 681 | { 682 | return ctx->inList; 683 | } 684 | 685 | /** 686 | Checks a function declaration for recursion and semantic analysis. 687 | 688 | @param ctx Pointer to the SemanticContext on which to perform checks. 689 | @return true if the function passes semantic analysis with no recursion, false otherwise 690 | */ 691 | bool NFunctionDeclaration::semanticAnalysis(SemanticContext * ctx) 692 | { 693 | bool blockSA, blockRecur, fr; 694 | if (ctx->inFunc) 695 | { 696 | std::cout << "Nested function declaration: " << ident << std::endl; 697 | return false; 698 | } 699 | ctx->newScope(type); 700 | for (auto it : variables) 701 | { 702 | if (!ctx->registerVar(it)) 703 | { 704 | ctx->delScope(); 705 | return false; 706 | } 707 | } 708 | if (body) 709 | { 710 | ctx->inFunc = true; 711 | blockSA = body->semanticAnalysis(ctx); 712 | blockRecur = body->checkRecursion(ctx, this); 713 | if (blockRecur) 714 | { 715 | std::cout << "Recursive function call in " << ident << std::endl; 716 | } 717 | fr = (type.typecode == VOID) ? true : ctx->funcReturns.back(); 718 | if (!fr) 719 | { 720 | std::cout << "Semantic ERROR: Control reaches end of non-void (" << type << ") function " << ident << std::endl; 721 | } 722 | ctx->inFunc = false; 723 | } 724 | else 725 | { 726 | blockSA = true; 727 | blockRecur = false; 728 | fr = true; 729 | } 730 | ctx->delScope(); 731 | return (blockSA && !blockRecur && fr); 732 | } 733 | 734 | /** 735 | Performs semantic analysis on an if statement and returns the boolean results. 736 | If the conditional statement cannot evaluate to a boolean, or if any of the code 737 | blocks in the if, else, elseif blocks is false, then the semantic analysis will 738 | return false. Function returns true if all checks pass. 739 | 740 | @param ctx Pointer to the SemanticContext on which to perform checks. 741 | @return true if semantic analysis is passed, false if conditional statement or blocks do no pass. 742 | */ 743 | bool NIfStatement::semanticAnalysis(SemanticContext * ctx) 744 | { 745 | Type & condType = condition.getType(ctx); 746 | if (condType.typecode == STRING || condType.typecode == INVALID || condType.typecode == VOID) 747 | { 748 | std::cout << "Condition cannot evaluate to a boolean!" << std::endl; 749 | return false; 750 | } 751 | if (elseblock && !elseblock->semanticAnalysis(ctx)) 752 | { 753 | return false; 754 | } 755 | if (elseif && !elseif->semanticAnalysis(ctx)) 756 | { 757 | return false; 758 | } 759 | return thenblock.semanticAnalysis(ctx); 760 | } 761 | 762 | /** 763 | Performs semantic analysis on a variable declaration by checking existing struct and variable 764 | names. Returns the boolean result of the check. 765 | 766 | @param ctx Pointer to the SemanticContext on which to perform checks. 767 | @return true if semantic analysis is passed, false if variables already exist or assignment type does not match. 768 | */ 769 | bool NVariableDeclaration::semanticAnalysis(SemanticContext * ctx) 770 | { 771 | if (type.isStruct) 772 | { 773 | StructType *st = (StructType *) &type; 774 | NStructureDeclaration *sd = ctx->searchStructs(st->ident); 775 | if (NULL == sd) 776 | { 777 | std::cout << "Declaring variable of undefined struct type: " << st->ident << std::endl; 778 | return false; 779 | } 780 | } 781 | 782 | if (!ctx->registerVar(this)) 783 | { 784 | std::cout << "Duplicate var decl for " << ident << std::endl; 785 | // Variable collision 786 | return false; 787 | } 788 | if (initializationExpression) 789 | { 790 | if (type < initializationExpression->getType(ctx)) 791 | { 792 | std::cout << "Type mismatch for " << ident << " (" << type << " vs. " << initializationExpression->getType(ctx) << ")" << std::endl; 793 | return false; 794 | } 795 | return initializationExpression->semanticAnalysis(ctx); 796 | } 797 | return true; 798 | } 799 | 800 | /** 801 | Performs semantic analysis on structure declaration by iterating over the members 802 | to make sure there are no duplicates. 803 | 804 | @param ctx Pointer to the SemanticContext on which to perform checks. 805 | @return true if semantic analysis is passed, false if duplicate var names exist 806 | */ 807 | bool NStructureDeclaration::semanticAnalysis(SemanticContext * ctx) 808 | { 809 | ctx->newScope(*(new Type())); 810 | for (auto it : members) 811 | { 812 | if (!ctx->registerVar(it)) 813 | { 814 | std::cout << "Duplicate struct member declaration for struct " << ident << std::endl; 815 | ctx->delScope(); 816 | return false; 817 | } 818 | } 819 | 820 | ctx->delScope(); 821 | return true; 822 | } 823 | 824 | /** 825 | Gets the Type object from a structure's member. If a varaible name is not found 826 | or if it does not have isStruct set to true, a new Type() object is returned. The 827 | for loop iterates over the members, finds the name, and returns its Type object. 828 | Default is a new Type() object. 829 | 830 | @param ctx Pointer to the SemanticContext on which to perform checks. 831 | @return Type object of struct member or new Type() object if not found. 832 | */ 833 | Type & NStructureAccess::getType(SemanticContext * ctx) const 834 | { 835 | NVariableDeclaration *var = ctx->searchVars(ident); 836 | if (!var) 837 | { 838 | return *(new Type()); 839 | } 840 | if (!(var->type.isStruct)) 841 | { 842 | return *(new Type()); 843 | } 844 | StructType *st = (StructType *) &(var->type); 845 | NStructureDeclaration *sd = ctx->searchStructs(st->ident); 846 | if (!sd) 847 | { 848 | return *(new Type()); 849 | } 850 | 851 | for (auto it : sd->members) 852 | if (member == it->ident) 853 | { 854 | type = it->type; 855 | return it->type; 856 | } 857 | 858 | return *(new Type()); 859 | } 860 | 861 | /** 862 | Performs semantic analysis check on structure member. If the struct member name 863 | cannot be found or if the struct name itself cannot be found, semanticAnalysis 864 | will return false. True otherwise. 865 | 866 | @param ctx Pointer to the SemanticContext on which to perform checks. 867 | @return true if semantic analysis passes, false if members or struct are not found. 868 | */ 869 | bool NStructureAccess::semanticAnalysis(SemanticContext * ctx) 870 | { 871 | NVariableDeclaration * var = ctx->searchVars(ident); 872 | if (NULL == var) 873 | { 874 | std::cout << "Structure variable " << ident << " cannot be found!" << std::endl; 875 | return false; 876 | } 877 | StructType *st = (StructType *) &(var->type); 878 | NStructureDeclaration * s = ctx->searchStructs(st->ident); 879 | if (NULL == s) 880 | { 881 | std::cout << "Reference to undefined structure " << st->ident << std::endl; 882 | return false; 883 | } 884 | 885 | for (auto it : s->members) 886 | if (it->ident == member) 887 | return true; 888 | 889 | std::cout << "Reference to non-existent member " << member << " of structure variable " << ident << std::endl; 890 | return false; 891 | } 892 | -------------------------------------------------------------------------------- /src/codegen.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | @file codegen.cpp 3 | @brief Contains routines for generating LLVM bytecode from an AST 4 | @copyright 2015 Assured Information Security, Inc. 5 | @author Jacob Torrey 6 | 7 | This file contains all the routines and logic to take a properly 8 | analyzed AST and generate LLVM IR bytecode 9 | */ 10 | 11 | #include "codegen.h" 12 | #include "ast.h" 13 | #include "parser.h" 14 | #include "types.h" 15 | 16 | CodeGenContext rootCodeGenCtx; 17 | std::map > structs; 18 | 19 | /** 20 | This constructor creates an llvm::Module object called 'rootModule' and a llvm::IRBuilder 21 | object called 'Builder'. A Module instance is used to store all the information related to 22 | an LLVM module. Modules are the top-level container of all other LLVM IR objects. Each 23 | module contains a list of global variables, functions, and libraries that it depends on, 24 | a symbol table, and data about the target's characteristics. The IRBuilder provides a uniform 25 | API for creating instructions and inserting them into a basic block. Use mutators 26 | (e.g. setVolatile) on instructions after they have been created for access to extra instruction 27 | properties. 28 | */ 29 | CodeGenContext::CodeGenContext() 30 | { 31 | rootModule = new llvm::Module("Crema JIT", llvm::getGlobalContext()); 32 | rootModule->setTargetTriple(llvm::sys::getDefaultTargetTriple()); 33 | Builder = new llvm::IRBuilder<>(llvm::getGlobalContext()); 34 | } 35 | 36 | /** 37 | Function to generate the LLVM IR bytecode for the program. 38 | llvm::ArrayRef -- constant reference to an array and allows various APIs to take consecutive 39 | elements easily and conveniently. 40 | llvm::Type -- instances are immutable and only one instance of a particular type is ever created. 41 | Thus, seeing if two types are equal is a pointer comparison. Once allocated, Types are never 42 | freed. 43 | llvm::FunctionType -- derived from llvm::Type 44 | llvm::Function -- derived from llvm::GlobalObject, immutable at runtime, because addess is 45 | immutable. 46 | llvm::BasicBlock -- a container of instructions that execute sequentially. A well-formed basic 47 | block has a list of non-terminating instructions followed by a single TerminatorInt instruction. 48 | variables is of type vector 49 | blocks is of type stack 50 | llvm::ReturnInst -- return a value (possibly void) from a function, and execution does not 51 | continue 52 | 53 | @param rootBlock Pointer to the root NBlock for the program 54 | */ 55 | void CodeGenContext::codeGen(NBlock * rootBlock) 56 | { 57 | Type * ct = new Type(); 58 | ct->typecode = CHAR; 59 | ct->isList = false; 60 | 61 | // Define arguments to root function, i.e. main(int, char**) 62 | std::vector params; 63 | params.push_back(llvm::Type::getInt64Ty(llvm::getGlobalContext())); 64 | params.push_back(llvm::PointerType::get(llvm::Type::getInt8PtrTy(llvm::getGlobalContext()), 0)); 65 | llvm::ArrayRef argTypes(params); 66 | 67 | // Create the root "function" for top level functionality 68 | llvm::FunctionType *ftype = llvm::FunctionType::get(llvm::Type::getInt64Ty(llvm::getGlobalContext()), argTypes, false); 69 | mainFunction = llvm::Function::Create(ftype, llvm::GlobalValue::ExternalLinkage, "main", rootModule); 70 | llvm::BasicBlock *bb = llvm::BasicBlock::Create(llvm::getGlobalContext(), "entry", mainFunction, 0); 71 | 72 | variables.push_back(*(new std::map >())); 73 | blocks.push(bb); 74 | if (rootBlock) 75 | { 76 | llvm::Function::arg_iterator args = mainFunction->arg_begin(); 77 | std::vector argvParseV; 78 | args->setName("argc"); 79 | argvParseV.push_back(args); 80 | args++; 81 | args->setName("argv"); 82 | argvParseV.push_back(args); 83 | llvm::ArrayRef argvParseR(argvParseV); 84 | 85 | // Create stdlib function (defined in stdlib/stdlib.c) 86 | // stdlib functions defined in ast.cpp are not yet available 87 | 88 | std::string funcname = "save_args"; 89 | llvm::FunctionType *ft = llvm::FunctionType::get(llvm::Type::getVoidTy(llvm::getGlobalContext()), argTypes, false); 90 | llvm::Function * func = llvm::Function::Create(ft, llvm::GlobalValue::ExternalLinkage, funcname.c_str(), this->rootModule); 91 | llvm::CallInst::Create(func, argvParseR, "", this->blocks.top()); 92 | 93 | // Call codeGen on our rootBlock 94 | rootBlock->codeGen(*this); 95 | } 96 | if (!blocks.top()->getTerminator()) 97 | llvm::ReturnInst::Create(llvm::getGlobalContext(), llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(64, 0, true)), blocks.top()); 98 | blocks.pop(); 99 | } 100 | 101 | /** 102 | This function is a quick and dirty way to execute an 'sitofp' instruction to double 103 | */ 104 | static inline llvm::CastInst* convertIToFP(llvm::Value * toConvert, CodeGenContext & context) 105 | { 106 | return new llvm::SIToFPInst(toConvert, llvm::Type::getDoubleTy(llvm::getGlobalContext()), "", context.blocks.top()); 107 | } 108 | 109 | /** 110 | A generic casting instruction generator 111 | 112 | @param val llvm::Value to cast 113 | @param expr Pointer to NExpression that generated the passed val 114 | @param type Type to convert to 115 | @param context CodeGenContext 116 | 117 | @return Generated llvm::Value * for casting instruction or error 118 | */ 119 | llvm::Value * convertToType(llvm::Value * val, NExpression * expr, Type & type, CodeGenContext & context) 120 | { 121 | if (expr->type == type) 122 | { 123 | return val; 124 | } 125 | if (expr->type.typecode == INT && type.typecode == DOUBLE) 126 | { 127 | return convertIToFP(val, context); 128 | } 129 | //if (expr.type.typecode == UINT && type.typecode == DOUBLE) 130 | return CodeGenError("Unable to generate casting instruction!"); 131 | } 132 | 133 | /** 134 | This function returns an instance of the llvm::BinaryOperator object generated with the Create function. 135 | This is a construction of a binary instruction given the opcode and the two operands. 136 | 137 | @param llvm::Instruction::BinaryOps i -- enum containing #defines and #includes 138 | @param CodeGenContext & context -- reference to the context of the operator statement 139 | @param NExpression & lhs -- lhs operand 140 | @param NExpression & rhs -- rhs operand 141 | @return llvm::Value * -- Pointer to an llvm::BinaryOperator instance containing instructions. 142 | */ 143 | static inline llvm::Value * binOpInstCreate(llvm::Instruction::BinaryOps i, CodeGenContext & context, NExpression & lhs, NExpression & rhs) 144 | { 145 | if (rhs.type == lhs.type) 146 | { 147 | return llvm::BinaryOperator::Create(i, lhs.codeGen(context), rhs.codeGen(context), "", context.blocks.top()); 148 | } 149 | Type & lt = Type::getLargerType(lhs.type, rhs.type); 150 | return llvm::BinaryOperator::Create(i, convertToType(lhs.codeGen(context), &lhs, lt, context), convertToType(rhs.codeGen(context), &rhs, lt, context), "", context.blocks.top()); 151 | } 152 | 153 | /** 154 | Creates an llvm::CmpInst::Create object, which constructs a compare instruction, given the predicate and two operands. The instruction is then 155 | inserted into a BasicBlock before the specified instruction. 156 | 157 | @param llvm::Instruction::OtherOps i -- an enum data structure 158 | @param unsigned short p -- an enum that lists the possible predicates for CmpInst subclasses 159 | @param CodeGenContext & context -- reference to the context of the operator statement 160 | @param NExpression & lhs -- lhs operand 161 | @param NExpression & rhs -- rhs operand 162 | @return llvm::Value * -- Pointer to an llvm::CmpInst instance containing instructions. 163 | */ 164 | static inline llvm::Value * cmpOpInstCreate(llvm::Instruction::OtherOps i, unsigned short p, CodeGenContext & context, NExpression & lhs, NExpression & rhs) 165 | { 166 | // ********* NOTE ********** 167 | // The OtherOps needs to be 168 | // mapped to the appropriate 169 | // tokens. 170 | // ************************* 171 | if (rhs.type == lhs.type) 172 | { 173 | return llvm::CmpInst::Create(i, p, lhs.codeGen(context), rhs.codeGen(context), "", context.blocks.top()); 174 | } 175 | Type & lt = Type::getLargerType(lhs.type, rhs.type); 176 | return llvm::CmpInst::Create(i, p, convertToType(lhs.codeGen(context), &lhs, lt, context), convertToType(rhs.codeGen(context), &rhs, lt, context), "", context.blocks.top()); 177 | } 178 | 179 | 180 | /** 181 | Function to execute a program after it's been generated using the LLVM JIT 182 | LLVMInitializeNativeTarget() -- initializes the native target corresponding to the host, useful to ensure target is linked correctly 183 | llvm::ExecutionEngine -- abstract interface for implementation execution of LLVM modules 184 | 185 | @return llvm::GenericValue object -- struct data structure 186 | */ 187 | llvm::GenericValue CodeGenContext::runProgram() 188 | { 189 | std::string err; 190 | LLVMInitializeNativeTarget(); 191 | llvm::ExecutionEngine *ee = llvm::EngineBuilder(rootModule).setEngineKind(llvm::EngineKind::Interpreter).setErrorStr(&err).create(); 192 | if (!ee) 193 | { 194 | std::cout << "Error: " << err << std::endl; 195 | exit(-1); 196 | } 197 | 198 | llvm::ArrayRef noargs; 199 | llvm::GenericValue retVal = ee->runFunction(mainFunction, noargs); 200 | 201 | return retVal; 202 | } 203 | 204 | /** 205 | Looks up a reference to a variable by iterating over the variables vector and searching for the string 206 | name of the variable (ident). 207 | 208 | @param ident String name of variable 209 | @return Pointer to llvm::Value of that variable, NULL if variable not found. 210 | */ 211 | llvm::Value * CodeGenContext::findVariable(std::string ident) 212 | { 213 | std::vector > >::reverse_iterator scopes; 214 | for ( scopes = variables.rbegin(); scopes != variables.rend(); scopes++) 215 | if ((*scopes).find(ident) != (*scopes).end()) 216 | return (*scopes).find(ident)->second.second; 217 | 218 | std::cout << "Unable to find variable " << ident << "!" << std::endl; 219 | return NULL; 220 | } 221 | 222 | /** 223 | Looks up a reference to a variable by iterating over the variables vector and searching for the string 224 | name of the variable (ident). 225 | 226 | @param ident String name of variable 227 | @return Pointer to NVariableDeclaration of that variable, NULL if variable not found. 228 | */ 229 | NVariableDeclaration * CodeGenContext::findVariableDeclaration(std::string ident) 230 | { 231 | std::vector > >::reverse_iterator scopes; 232 | for ( scopes = variables.rbegin(); scopes != variables.rend(); scopes++) 233 | if ((*scopes).find(ident) != (*scopes).end()) 234 | return (*scopes).find(ident)->second.first; 235 | 236 | std::cout << "Unable to find variable " << ident << "!" << std::endl; 237 | return NULL; 238 | } 239 | 240 | /** 241 | Adds a variable name/Value * pair to the stack of scopes 242 | 243 | @param ident String name of variable 244 | @param value Pointer to llvm::Value to store 245 | */ 246 | void CodeGenContext::addVariable(NVariableDeclaration * var, llvm::Value * value) 247 | { 248 | variables.back()[var->ident.value] = *(new std::pair(var, value)); 249 | } 250 | 251 | /** 252 | An error function for code generation routines 253 | 254 | @param str A string to print as an error message 255 | @return NULL llvm:Value pointer 256 | */ 257 | llvm::Value * CodeGenError(const char * str) 258 | { 259 | std::cout << "ERROR: " << str << std::endl; 260 | return NULL; 261 | } 262 | 263 | /** 264 | Iterates over the StatementList statements vector (typedef std::vector StatementList) and 265 | returns a pointer to the last statement in the context. 266 | 267 | @param CodeGenContext & context -- reference to the context of the operator statement 268 | @return llvm::Value * -- Pointer to the last statement in the codeGen(context) 269 | */ 270 | llvm::Value * NBlock::codeGen(CodeGenContext & context) 271 | { 272 | llvm::Value * last; 273 | for (auto it : statements) 274 | last = (it)->codeGen(context); 275 | 276 | return last; 277 | } 278 | 279 | /** 280 | Generates code to declare a structure. This will take the format of the structure and create a new 281 | LLVM StructType which is later used in the codeGen method for NVariableDeclaration. 282 | 283 | @param context CodeGenContext parameter 284 | @return Returns nothing concretely, but adds the structure type to the struct map 285 | */ 286 | llvm::Value * NStructureDeclaration::codeGen(CodeGenContext & context) 287 | { 288 | std::vector vec; 289 | for (int i = 0; i < members.size(); i++) 290 | { 291 | vec.push_back(members[i]->type.toLlvmType()); 292 | } 293 | llvm::ArrayRef mems(vec); 294 | structs[ident.value] = *(new std::pair(this, llvm::StructType::create(llvm::getGlobalContext(), mems, ident.value, false))); 295 | } 296 | 297 | /** 298 | Generates code for looping constructs 299 | 300 | @param context Reference of the CodeGenContext 301 | @return llvm::Value * pointing to the generated instructions 302 | */ 303 | llvm::Value * NLoopStatement::codeGen(CodeGenContext & context) 304 | { 305 | NIdentifier * itIdent = new NIdentifier("loopItCnter"); 306 | NIdentifier * maxIdent = new NIdentifier("loopItMax"); 307 | NVariableDeclaration * loop = context.findVariableDeclaration(list.value); 308 | NVariableDeclaration * loopVar = new NVariableDeclaration(*(new Type(loop->type, false)), asVar, NULL); 309 | NVariableDeclaration * itNum = new NVariableDeclaration(*(new Type(TTINT)), *itIdent, new NInt(0)); 310 | 311 | std::vector args; 312 | args.push_back(new NVariableAccess(list)); 313 | NVariableAccess * access = new NVariableAccess(*itIdent); 314 | access->type = itNum->type; 315 | NFunctionCall * funcCall = new NFunctionCall(*(new NIdentifier("list_length")), args); 316 | funcCall->type = itNum->type; 317 | NVariableDeclaration * loopMax = new NVariableDeclaration(*(new Type(TTINT)), *maxIdent, ((NExpression *) funcCall)); 318 | NVariableAccess * maxaccess = new NVariableAccess(*maxIdent); 319 | maxaccess->type = itNum->type; 320 | NBinaryOperator * c = new NBinaryOperator(*((NExpression *) access), (int) TCEQ, *((NExpression *) maxaccess)); 321 | llvm::Value * cond; 322 | llvm::Function * parent = context.blocks.top()->getParent(); 323 | llvm::BasicBlock * preBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "preblock", parent); 324 | llvm::BasicBlock * bodyBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "bodyblock", parent); 325 | llvm::BasicBlock * loopCondBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "loopcondblock", parent); 326 | llvm::BasicBlock * terminateBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "termblock"); 327 | 328 | // Create pre-block 329 | context.blocks.push(preBlock); 330 | context.listblocks.push(terminateBlock); 331 | context.Builder->SetInsertPoint(context.blocks.top()); 332 | llvm::Value * itNumBC = itNum->codeGen(context); 333 | llvm::Value * lvBC = loopVar->codeGen(context); 334 | llvm::Value * maxNumBC = loopMax->codeGen(context); 335 | llvm::BranchInst::Create(bodyBlock, context.blocks.top()->end()); 336 | context.blocks.pop(); 337 | 338 | context.Builder->SetInsertPoint(context.blocks.top()); 339 | std::cout << "Creating branch to preBlock" << std::endl; 340 | std::cout << "pb: " << preBlock->getName().str() << " " << context.blocks.top()->getName().str() << std::endl; 341 | llvm::BranchInst::Create(preBlock, context.blocks.top()); 342 | 343 | std::cout << "Creating body block" << std::endl; 344 | context.blocks.push(bodyBlock); 345 | context.Builder->SetInsertPoint(context.blocks.top()); 346 | context.variables.push_back(*(new std::map >())); 347 | // Add asVar to context 348 | context.addVariable(loopVar, lvBC); 349 | 350 | std::cout << "Creating list access instruction" << std::endl; 351 | NListAccess * nla = new NListAccess(list, access); 352 | nla->type = loop->type; 353 | new llvm::StoreInst(nla->codeGen(context), lvBC, false, context.blocks.top()); 354 | 355 | std::cout << "Generating body" << std::endl; 356 | llvm::Value * bodyval = loopBlock.codeGen(context); 357 | 358 | if (!context.blocks.top()->getTerminator()) { 359 | llvm::BranchInst::Create(loopCondBlock, context.blocks.top()); 360 | } 361 | 362 | while (bodyBlock != context.blocks.top()) 363 | context.blocks.pop(); 364 | context.listblocks.pop(); 365 | context.blocks.pop(); 366 | context.variables.pop_back(); 367 | 368 | context.blocks.push(loopCondBlock); 369 | context.Builder->SetInsertPoint(context.blocks.top()); 370 | 371 | // Increment loop counter 372 | NInt one(1); 373 | 374 | llvm::Value * newValue = llvm::BinaryOperator::Create(llvm::Instruction::Add, one.codeGen(context), new llvm::LoadInst(itNumBC, "", false, context.blocks.top()), "", context.blocks.top()); 375 | new llvm::StoreInst(newValue, itNumBC, false, context.blocks.top()); 376 | 377 | // Create termination check 378 | cond = c->codeGen(context); 379 | llvm::BranchInst::Create(terminateBlock, bodyBlock, cond, context.blocks.top()); 380 | 381 | context.blocks.pop(); 382 | 383 | // Link in the terminate block to the function 384 | context.blocks.push(terminateBlock); 385 | parent->getBasicBlockList().push_back(terminateBlock); 386 | context.Builder->SetInsertPoint(context.blocks.top()); 387 | return cond; 388 | } 389 | 390 | /** 391 | Generates code for a break statement to "break" out of a single loop control-flow 392 | 393 | @param context Reference of codegen context 394 | @return Pointer to generated branch instruction to break out of loop 395 | */ 396 | llvm::Value * NBreak::codeGen(CodeGenContext & context) 397 | { 398 | // llvm::Function * parent = context.blocks.top()->getParent(); 399 | // llvm::BasicBlock * brkBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "breakblock"); 400 | //parent->getBasicBlockList().push_back(brkBlock); 401 | llvm::Value * bi = llvm::BranchInst::Create(context.listblocks.top(), context.blocks.top()); 402 | 403 | //context.blocks.push(brkBlock); 404 | return bi; 405 | } 406 | 407 | /** 408 | Generates the code for if/if-then/if-then-else statements. A switch statement assigns the value to an llvm::Value * cond variable. 409 | Then pointers to the parent block and the then, else, and ifcond blocks are created. The code then goes through and builds the 410 | set of instructions according to the construction of the if statement. 411 | 412 | @param CodeGenContext & context -- reference to the context of the operator statement 413 | @return llvm::Value * -- Pointer to the llvm::Value object containing the generated code 414 | */ 415 | llvm::Value * NIfStatement::codeGen(CodeGenContext & context) 416 | { 417 | llvm::Value * cond = condition.codeGen(context); 418 | switch (condition.type.typecode) 419 | { 420 | case DOUBLE: 421 | cond = llvm::CmpInst::Create(llvm::Instruction::FCmp, llvm::CmpInst::FCMP_ONE, llvm::ConstantFP::get(llvm::getGlobalContext(), llvm::APFloat(0.0)), cond, "", context.blocks.top()); 422 | break; 423 | case UINT: 424 | case INT: 425 | cond = llvm::CmpInst::Create(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_SLT, llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(64, 0, false)), cond, "", context.blocks.top()); 426 | break; 427 | case BOOL: 428 | if (cond == NULL) { 429 | std::cout << "Failed to generate boolean conditional" << std::endl; 430 | exit(-1); 431 | } 432 | if (typeid(condition) != typeid(NBinaryOperator)) { 433 | cond = llvm::CmpInst::Create(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_NE, llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(1, 0, false)), cond, "", context.blocks.top()); 434 | } 435 | break; 436 | default: 437 | cond = NULL; 438 | std::cout << "Error, unable to emit conditional bytecode for type: " << condition.type << std::endl; 439 | return cond; 440 | break; 441 | } 442 | 443 | llvm::Function * parent = context.blocks.top()->getParent(); 444 | llvm::BasicBlock * thenBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "", parent, 0); 445 | llvm::BasicBlock * elseBlock = NULL; 446 | llvm::BasicBlock * ifcontBlock = NULL; 447 | 448 | if (elseblock || elseif) { 449 | elseBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), ""); 450 | llvm::BranchInst::Create(thenBlock, elseBlock, cond, context.blocks.top()); 451 | } 452 | 453 | ifcontBlock = llvm::BasicBlock::Create(llvm::getGlobalContext(), ""); 454 | if (elseBlock == NULL) { 455 | llvm::BranchInst::Create(thenBlock, ifcontBlock, cond, context.blocks.top()); 456 | } 457 | 458 | // THEN BLOCK 459 | context.blocks.push(thenBlock); 460 | context.Builder->SetInsertPoint(thenBlock); 461 | 462 | context.variables.push_back(*(new std::map >())); 463 | llvm::Value * thenValue = thenblock.codeGen(context); 464 | 465 | if (!context.blocks.top()->getTerminator()) 466 | context.Builder->CreateBr(ifcontBlock); 467 | 468 | // POP THEN 469 | while (context.blocks.top() != thenBlock) 470 | context.blocks.pop(); 471 | context.blocks.pop(); 472 | context.variables.pop_back(); 473 | 474 | if (elseblock || elseif) { 475 | // ELSE BLOCK 476 | // All nested blocks and branches in thenBlock are linked 477 | // it is now safe to insert the elseBlock 478 | parent->getBasicBlockList().push_back(elseBlock); 479 | context.blocks.push(elseBlock); 480 | context.Builder->SetInsertPoint(elseBlock); 481 | 482 | context.variables.push_back(*(new std::map >())); 483 | llvm::Value * elseValue = NULL; 484 | if (elseblock) 485 | elseValue = elseblock->codeGen(context); 486 | else if(elseif) 487 | elseValue = elseif->codeGen(context); 488 | 489 | if (!context.blocks.top()->getTerminator()) 490 | context.Builder->CreateBr(ifcontBlock); 491 | 492 | // POP ELSE 493 | while (!context.blocks.empty() && context.blocks.top() != elseBlock) 494 | context.blocks.pop(); 495 | context.blocks.pop(); 496 | context.variables.pop_back(); 497 | } 498 | parent->getBasicBlockList().push_back(ifcontBlock); 499 | context.blocks.push(ifcontBlock); 500 | context.Builder->SetInsertPoint(ifcontBlock); 501 | /* 502 | llvm::PHINode *PN = context.Builder->CreatePHI(llvm::Type::getVoidTy(llvm::getGlobalContext()), 2, "iftmp"); 503 | 504 | PN->addIncoming(thenValue, thenBlock); 505 | PN->addIncoming(ev, elseBlock); 506 | 507 | return PN; 508 | */ 509 | return cond; 510 | } 511 | 512 | /** 513 | Generates the code to read a variable from memory. The 'false' flag indicates that the variable is NOT volatile. 514 | 515 | @param CodeGenContext & context -- reference to the context of the operator statement 516 | @return llvm::Value * -- Pointer to the code of the loaded instruction that was generated. 517 | */ 518 | llvm::Value * NVariableAccess::codeGen(CodeGenContext & context) 519 | { 520 | llvm::Value * var = context.findVariable(ident.value); 521 | llvm::Value * loadedInst = new llvm::LoadInst(var, "", false, context.blocks.top()); 522 | return loadedInst; 523 | } 524 | 525 | /** 526 | Generates the instruction code for storing to memory. The 'false' flag indicates that the variable is NOT volatile. 527 | 528 | @param CodeGenContext & context -- reference to the context of the operator statement 529 | @return llvm::Value * -- Pointer to the code of the instruction that was generated. 530 | */ 531 | llvm::Value * NAssignmentStatement::codeGen(CodeGenContext & context) 532 | { 533 | return new llvm::StoreInst(expr.codeGen(context), context.findVariable(ident.value), false, context.blocks.top()); 534 | } 535 | 536 | /** 537 | Generates the code for binary and comparison statements. 538 | 539 | @param CodeGenContext & context -- reference to the context of the operator statement 540 | @return llvm::Value * -- Pointer to the code of the binary or comparison expression that was generated. 541 | */ 542 | llvm::Value * NBinaryOperator::codeGen(CodeGenContext & context) 543 | { 544 | TypeCodes tc = Type::getLargerType(rhs.type, lhs.type).typecode; 545 | 546 | switch (op) 547 | { 548 | // Math operations 549 | case TADD: 550 | case TLOR: 551 | if (tc == DOUBLE) 552 | return binOpInstCreate(llvm::Instruction::FAdd, context, lhs, rhs); 553 | if (tc == INT || tc == BOOL) 554 | { 555 | type.typecode = INT; 556 | return binOpInstCreate(llvm::Instruction::Add, context, lhs, rhs); 557 | } 558 | break; 559 | case TSUB: 560 | if (tc == DOUBLE) 561 | return binOpInstCreate(llvm::Instruction::FSub, context, lhs, rhs); 562 | if (tc == INT) 563 | return binOpInstCreate(llvm::Instruction::Sub, context, lhs, rhs); 564 | break; 565 | case TMUL: 566 | case TLAND: 567 | if (tc == DOUBLE) 568 | return binOpInstCreate(llvm::Instruction::FMul, context, lhs, rhs); 569 | if (tc == INT || tc == BOOL) 570 | { 571 | type.typecode = INT; 572 | return binOpInstCreate(llvm::Instruction::Mul, context, lhs, rhs); 573 | } 574 | break; 575 | case TDIV: 576 | if (tc == DOUBLE) 577 | return binOpInstCreate(llvm::Instruction::FDiv, context, lhs, rhs); 578 | if (tc == INT) 579 | return binOpInstCreate(llvm::Instruction::SDiv, context, lhs, rhs); 580 | break; 581 | case TMOD: 582 | if (tc == DOUBLE) 583 | return binOpInstCreate(llvm::Instruction::FRem, context, lhs, rhs); 584 | if (tc == INT) 585 | return binOpInstCreate(llvm::Instruction::SRem, context, lhs, rhs); 586 | break; 587 | case TBAND: 588 | return binOpInstCreate(llvm::Instruction::And, context, lhs, rhs); 589 | break; 590 | case TBOR: 591 | return binOpInstCreate(llvm::Instruction::Or, context, lhs, rhs); 592 | break; 593 | case TBXOR: 594 | return binOpInstCreate(llvm::Instruction::Xor, context, lhs, rhs); 595 | break; 596 | 597 | // Comparison operations 598 | case TCEQ: 599 | if (tc == DOUBLE) 600 | return cmpOpInstCreate(llvm::Instruction::FCmp, llvm::CmpInst::FCMP_OEQ, context, lhs, rhs); 601 | if ((tc == INT || tc == CHAR || tc == BOOL) && rhs.type == lhs.type) 602 | return cmpOpInstCreate(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_EQ, context, lhs, rhs); 603 | return NULL; 604 | break; 605 | 606 | case TCNEQ: 607 | if (tc == DOUBLE) 608 | return cmpOpInstCreate(llvm::Instruction::FCmp, llvm::CmpInst::FCMP_ONE, context, lhs, rhs); 609 | if (tc == INT || tc == CHAR || tc == BOOL) 610 | return cmpOpInstCreate(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_NE, context, lhs, rhs); 611 | return NULL; 612 | break; 613 | 614 | case TCLT: 615 | if (tc == DOUBLE) 616 | return cmpOpInstCreate(llvm::Instruction::FCmp, llvm::CmpInst::FCMP_OLT, context, lhs, rhs); 617 | if (tc == INT || tc == CHAR) 618 | return cmpOpInstCreate(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_SLT, context, lhs, rhs); 619 | return NULL; 620 | break; 621 | 622 | case TCGT: 623 | if (tc == DOUBLE) 624 | return cmpOpInstCreate(llvm::Instruction::FCmp, llvm::CmpInst::FCMP_OGT, context, lhs, rhs); 625 | if (tc == INT || tc == CHAR) 626 | return cmpOpInstCreate(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_SGT, context, lhs, rhs); 627 | return NULL; 628 | break; 629 | 630 | case TCLE: 631 | if (tc == DOUBLE) 632 | return cmpOpInstCreate(llvm::Instruction::FCmp, llvm::CmpInst::FCMP_OLE, context, lhs, rhs); 633 | if (tc == INT || tc == CHAR) 634 | return cmpOpInstCreate(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_SLE, context, lhs, rhs); 635 | return NULL; 636 | break; 637 | 638 | case TCGE: 639 | if (tc == DOUBLE) 640 | return cmpOpInstCreate(llvm::Instruction::FCmp, llvm::CmpInst::FCMP_OGE, context, lhs, rhs); 641 | if (tc == INT || tc == CHAR) 642 | return cmpOpInstCreate(llvm::Instruction::ICmp, llvm::CmpInst::ICMP_SGE, context, lhs, rhs); 643 | return NULL; 644 | break; 645 | 646 | default: 647 | return NULL; 648 | } 649 | } 650 | 651 | /** 652 | Generates the stdlib function call bytecode to retrieve an element from a list 653 | 654 | @param CodeGenContext & context -- reference to the context of the operator statement 655 | @return llvm::Value * -- Pointer to the code generated that will access a list elemnt. 656 | */ 657 | llvm::Value * NListAccess::codeGen(CodeGenContext & context) 658 | { 659 | llvm::Value * var = context.findVariable(ident.value); 660 | std::string name; 661 | if (!index) 662 | { 663 | std::cout << "NULL index for NListAccess!" << std::endl; 664 | return NULL; 665 | } 666 | switch (type.typecode) 667 | { 668 | case INT: 669 | name = "int_list_retrieve"; 670 | break; 671 | case CHAR: 672 | case STRING: 673 | name = "str_retrieve"; 674 | break; 675 | default: 676 | return NULL; 677 | } 678 | llvm::Function *func = context.rootModule->getFunction(name.c_str()); 679 | llvm::Value * li = new llvm::LoadInst(var, "", false, context.blocks.top()); 680 | std::vector v; 681 | v.push_back(li); 682 | // Generate LLVM IR for the list index 683 | llvm::Value *igc = index->codeGen(context); 684 | v.push_back(igc); 685 | 686 | llvm::ArrayRef llvmargs(v); 687 | return llvm::CallInst::Create(func, llvmargs, "", context.blocks.top()); 688 | } 689 | 690 | /** 691 | Generates the stdlib function call bytecode to insert an element into a list 692 | 693 | @param CodeGenContext & context -- reference to the context of the operator statement 694 | @return llvm::Value * -- Pointer to the code generated that will insert into a list elemnt. 695 | */ 696 | llvm::Value * NListAssignmentStatement::codeGen(CodeGenContext & context) 697 | { 698 | llvm::Value * var = context.findVariable(list.ident.value); 699 | std::string name; 700 | switch (list.type.typecode) 701 | { 702 | case INT: 703 | if (list.index) 704 | { 705 | name = "int_list_insert"; 706 | } 707 | else 708 | { 709 | name = "int_list_append"; 710 | } 711 | break; 712 | case STRING: 713 | case CHAR: 714 | if (list.index) 715 | { 716 | name = "str_insert"; 717 | } 718 | else 719 | { 720 | name = "str_append"; 721 | } 722 | break; 723 | case DOUBLE: 724 | if (list.index) 725 | { 726 | name = "double_list_insert"; 727 | } 728 | else 729 | { 730 | name = "double_list_append"; 731 | } 732 | break; 733 | default: 734 | std::cout << "Unable to assign list for type: " << list.type.typecode << std::endl; 735 | return NULL; 736 | } 737 | llvm::Function *func = context.rootModule->getFunction(name.c_str()); 738 | llvm::Value * li = new llvm::LoadInst(var, "", false, context.blocks.top()); 739 | std::vector v; 740 | v.push_back(li); 741 | if (list.index) 742 | { 743 | v.push_back(list.index->codeGen(context)); 744 | } 745 | v.push_back(expr.codeGen(context)); 746 | 747 | llvm::ArrayRef llvmargs(v); 748 | return llvm::CallInst::Create(func, llvmargs, "", context.blocks.top()); 749 | } 750 | 751 | static llvm::GetElementPtrInst * getGEPForStruct(llvm::Value * var, NIdentifier & member, NStructureDeclaration * sd, CodeGenContext & context) 752 | { 753 | int i; 754 | for (i = 0; i < sd->members.size(); i++) 755 | { 756 | if (member == sd->members[i]->ident) 757 | { 758 | break; 759 | } 760 | } 761 | 762 | std::vector vec; 763 | // The argument IdxList *MUST* contain i32 values otherwise the call to GEP::Create() will segfault 764 | vec.push_back(llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(32, 0, true))); 765 | vec.push_back(llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(32, i, true))); 766 | 767 | llvm::ArrayRef arr(vec); 768 | llvm::GetElementPtrInst * gep = llvm::GetElementPtrInst::Create(var, arr, "", context.blocks.top()); 769 | if (!gep) 770 | { 771 | std::cout << "Error: Unable to make GEP instruction!" << std::endl; 772 | exit(-1); 773 | } 774 | return gep; 775 | } 776 | 777 | /** 778 | Generates LLVM IR byte-code for accessing a structure field 779 | 780 | @param context Reference to CodeGenContext 781 | @return Pointer to generated llvm::Value or NULL if code cannot be generated 782 | */ 783 | llvm::Value * NStructureAccess::codeGen(CodeGenContext & context) 784 | { 785 | llvm::Value * var = context.findVariable(ident.value); 786 | NVariableDeclaration * vd = context.findVariableDeclaration(ident.value); 787 | if (!vd || !var) 788 | { 789 | std::cout << "Error: Unable to find variable for " << ident << std::endl; 790 | exit(-1); 791 | } 792 | StructType *st = (StructType *) &(vd->type); 793 | NStructureDeclaration * sd = structs[st->ident.value].first; 794 | llvm::GetElementPtrInst * gep = getGEPForStruct(var, member, sd, context); 795 | return new llvm::LoadInst(gep, "", false, context.blocks.top()); 796 | } 797 | 798 | /** 799 | Generates the instruction code for storing to memory. The 'false' flag indicates that the variable is NOT volatile. 800 | 801 | @param CodeGenContext & context -- reference to the context of the operator statement 802 | @return llvm::Value * -- Pointer to the code of the instruction that was generated. 803 | */ 804 | llvm::Value * NStructureAssignmentStatement::codeGen(CodeGenContext & context) 805 | { 806 | llvm::Value * var = context.findVariable(structure.ident.value); 807 | NVariableDeclaration * vd = context.findVariableDeclaration(structure.ident.value); 808 | if (!vd || !var) 809 | { 810 | std::cout << "Error: Unable to find variable for " << structure.ident << std::endl; 811 | exit(-1); 812 | } 813 | StructType *st = (StructType *) &(vd->type); 814 | NStructureDeclaration * sd = structs[st->ident.value].first; 815 | llvm::GetElementPtrInst * gep = getGEPForStruct(var, structure.member, sd, context); 816 | return new llvm::StoreInst(expr.codeGen(context), gep, false, context.blocks.top()); 817 | } 818 | 819 | /** 820 | Generates the necessary code for defining a function. 821 | 822 | @param CodeGenContext & context -- reference to the context of the operator statement 823 | @return llvm::Value * -- Pointer to the code generated that will declare a function. 824 | */ 825 | llvm::Value * NFunctionDeclaration::codeGen(CodeGenContext & context) 826 | { 827 | std::vector v; 828 | // Loop through argument types 829 | for (auto it : variables) 830 | v.push_back(it->type.toLlvmType()); 831 | 832 | // Convert from std::vector to llvm::ArrayRef 833 | llvm::ArrayRef argtypes(v); 834 | llvm::FunctionType *ft = llvm::FunctionType::get(type.toLlvmType(), argtypes, false); 835 | llvm::Function * func; 836 | 837 | if (body) 838 | { 839 | std::cout << "Generating function body: " << ident.value.c_str() << std::endl; 840 | func = llvm::Function::Create(ft, llvm::GlobalValue::InternalLinkage, ident.value.c_str(), context.rootModule); 841 | llvm::BasicBlock *bb = llvm::BasicBlock::Create(llvm::getGlobalContext(), "entry", func); 842 | 843 | context.blocks.push(bb); 844 | context.variables.push_back(*(new std::map >())); 845 | context.Builder->SetInsertPoint(bb); 846 | 847 | int i = 0; 848 | for (llvm::Function::arg_iterator args = func->arg_begin(); args != func->arg_end(); ++args) 849 | { 850 | new llvm::StoreInst(args, variables[i]->codeGen(context), false, context.blocks.top()); 851 | i++; 852 | } 853 | 854 | body->codeGen(context); 855 | 856 | if (type.typecode == VOID) 857 | { 858 | // Add in a void return instruction for void functions 859 | llvm::ReturnInst::Create(llvm::getGlobalContext(), context.blocks.top()); 860 | } else if (!context.blocks.top()->getTerminator()) { 861 | std::cout << "Warning: Control may reach end of non-void function: " << ident << std::endl; 862 | } 863 | 864 | while (context.blocks.empty() == false && context.blocks.top() != bb) { 865 | context.blocks.pop(); 866 | } 867 | if (context.blocks.empty() == false && context.blocks.top() == bb) 868 | context.blocks.pop(); 869 | context.variables.pop_back(); 870 | } 871 | else 872 | { 873 | func = llvm::Function::Create(ft, llvm::GlobalValue::ExternalLinkage, ident.value.c_str(), context.rootModule); 874 | } 875 | return func; 876 | } 877 | 878 | /** 879 | Generates the necessary code needed to call a defined function. 880 | 881 | @param CodeGenContext & context -- reference to the context of the operator statement 882 | @return llvm::Value * -- Pointer to the code generated that will call a function. 883 | */ 884 | llvm::Value * NFunctionCall::codeGen(CodeGenContext & context) 885 | { 886 | llvm::Function *func = context.rootModule->getFunction(ident.value.c_str()); 887 | std::vector v; 888 | for (auto it : args) 889 | v.push_back(it->codeGen(context)); 890 | 891 | llvm::ArrayRef llvmargs(v); 892 | return llvm::CallInst::Create(func, llvmargs, "", context.blocks.top()); 893 | } 894 | 895 | /** 896 | Generates the necessary code needed to execute a return statement. 897 | 898 | @param CodeGenContext & context -- reference to the context of the operator statement 899 | @return llvm::Value * -- Pointer to the code generated that will execute a return statement. 900 | */ 901 | llvm::Value * NReturn::codeGen(CodeGenContext & context) 902 | { 903 | llvm::Value *re = retExpr.codeGen(context); 904 | 905 | // upcasts the return value to floating point if function return is 906 | // declared as double, but integer is returned in body of function 907 | if ( retExpr.type.toLlvmType() != context.blocks.top()->getParent()->getReturnType() ) 908 | return llvm::ReturnInst::Create(llvm::getGlobalContext(), convertIToFP(re,context), context.blocks.top()); 909 | 910 | return llvm::ReturnInst::Create(llvm::getGlobalContext(), re, context.blocks.top()); 911 | } 912 | 913 | /** 914 | Generates the code for allocating memory and declaring a variable. 915 | 916 | @param CodeGenContext & context -- reference to the context of the operator statement 917 | @return llvm::Value * -- Pointer to the code generated that will execute a return statement. 918 | */ 919 | llvm::Value * NVariableDeclaration::codeGen(CodeGenContext & context) 920 | { 921 | llvm::Value * a; 922 | if (type.isStruct) 923 | { 924 | StructType *st = (StructType *) &type; 925 | if (context.blocks.top()->getParent()->getName().str() == "main") 926 | { 927 | a = new llvm::GlobalVariable(*(context.rootModule), structs[st->ident.value].second, false, llvm::GlobalValue::InternalLinkage, llvm::UndefValue::get(structs[st->ident.value].second), ident.value); 928 | } 929 | else 930 | { 931 | a = new llvm::AllocaInst(structs[st->ident.value].second, ident.value, context.blocks.top()); 932 | } 933 | } 934 | else 935 | { 936 | if (context.blocks.top()->getParent()->getName().str() == "main") 937 | { 938 | a = new llvm::GlobalVariable(*(context.rootModule), type.toLlvmType(), false, llvm::GlobalValue::InternalLinkage, llvm::UndefValue::get(type.toLlvmType()), ident.value); 939 | } 940 | else 941 | { 942 | a = new llvm::AllocaInst(type.toLlvmType(), ident.value, context.blocks.top()); 943 | } 944 | if ((type.isList || type.typecode == STRING) && !initializationExpression) 945 | { 946 | // TODO codegen 947 | std::string name; 948 | switch (type.typecode) 949 | { 950 | case INT: 951 | name = "int_list_create"; 952 | break; 953 | case STRING: 954 | case CHAR: 955 | name = "str_create"; 956 | break; 957 | default: 958 | std::cout << "Unable to create list for type: " << type << std::endl; 959 | return NULL; 960 | } 961 | llvm::Function *func = context.rootModule->getFunction(name.c_str()); 962 | std::vector v; 963 | 964 | llvm::ArrayRef llvmargs(v); 965 | llvm::Value * si = new llvm::StoreInst(llvm::CallInst::Create(func, llvmargs, "", context.blocks.top()), a, false, context.blocks.top()); 966 | } 967 | } 968 | context.addVariable(this, a); 969 | 970 | if (NULL != initializationExpression) 971 | { 972 | NAssignmentStatement nas(ident, *initializationExpression); 973 | nas.codeGen(context); 974 | } 975 | 976 | return a; 977 | } 978 | 979 | /** 980 | Generates code for a pre-defined list 981 | 982 | @param context Context variable 983 | @return LLVM bytecode for the list 984 | */ 985 | llvm::Value * NList::codeGen(CodeGenContext & context) 986 | { 987 | // Create list 988 | std::string name; 989 | switch (type.typecode) 990 | { 991 | case INT: 992 | name = "int_list_create"; 993 | break; 994 | case CHAR: 995 | name = "str_create"; 996 | break; 997 | case DOUBLE: 998 | name = "double_list_create"; 999 | break; 1000 | default: 1001 | std::cout << "Unsupported type for list!" << std::endl; 1002 | exit(-1); 1003 | return NULL; 1004 | } 1005 | llvm::Function *func = context.rootModule->getFunction(name.c_str()); 1006 | std::vector v; 1007 | 1008 | llvm::ArrayRef llvmargs(v); 1009 | llvm::Value * li = llvm::CallInst::Create(func, llvmargs, "", context.blocks.top()); 1010 | 1011 | for (int i = 0; i < value.size(); i++) 1012 | { 1013 | switch (type.typecode) 1014 | { 1015 | case INT: 1016 | name = "int_list_append"; 1017 | break; 1018 | case CHAR: 1019 | name = "str_append"; 1020 | break; 1021 | case DOUBLE: 1022 | name = "double_list_append"; 1023 | break; 1024 | default: 1025 | return NULL; 1026 | } 1027 | llvm::Function *func = context.rootModule->getFunction(name.c_str()); 1028 | std::vector v; 1029 | v.push_back(li); 1030 | v.push_back(value[i]->codeGen(context)); 1031 | 1032 | llvm::ArrayRef llvmargs(v); 1033 | llvm::CallInst::Create(func, llvmargs, "", context.blocks.top()); 1034 | } 1035 | 1036 | return li; 1037 | } 1038 | 1039 | /** 1040 | Generates code for string values. 1041 | 1042 | @param CodeGenContext & context -- reference to the context of the operator statement 1043 | @return llvm::Value * -- Pointer to the code generated as a result of the string instance 1044 | */ 1045 | llvm::Value * NString::codeGen(CodeGenContext & context) 1046 | { 1047 | ExpressionList * chars = new ExpressionList(); 1048 | for (int i = 0; i < value.length(); i++) 1049 | { 1050 | NChar *c = new NChar(value[i]); 1051 | c->type.typecode = CHAR; 1052 | chars->push_back(c); 1053 | } 1054 | NList * cl = new NList(*chars); 1055 | cl->type.typecode = CHAR; 1056 | llvm::Value * v = cl->codeGen(context); 1057 | return v; 1058 | } 1059 | 1060 | /** 1061 | Generates code for floating point values. 1062 | 1063 | @param CodeGenContext & context -- reference to the context of the operator statement 1064 | @return llvm::Value * -- Pointer to the code generated as a result of the floating point instance 1065 | */ 1066 | llvm::Value * NDouble::codeGen(CodeGenContext & context) 1067 | { 1068 | return llvm::ConstantFP::get(llvm::getGlobalContext(), llvm::APFloat(value)); 1069 | } 1070 | 1071 | /** 1072 | Generates code for unsigned int values. 1073 | 1074 | @param CodeGenContext & context -- reference to the context of the operator statement 1075 | @return llvm::Value * -- Pointer to the code generated as a result of the unsigned integer instance 1076 | */ 1077 | llvm::Value * NUInt::codeGen(CodeGenContext & context) 1078 | { 1079 | return llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(64, value, false)); 1080 | } 1081 | 1082 | /** 1083 | Generates code for signed integer values. 1084 | 1085 | @param CodeGenContext & context -- reference to the context of the operator statement 1086 | @return llvm::Value * -- Pointer to the code generated as a result of the signed integer instance 1087 | */ 1088 | llvm::Value * NInt::codeGen(CodeGenContext & context) 1089 | { 1090 | return llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(64, value, true)); 1091 | } 1092 | 1093 | /** 1094 | Generates code for signed char values. 1095 | 1096 | @param CodeGenContext & context -- reference to the context of the operator statement 1097 | @return llvm::Value * -- Pointer to the code generated as a result of the signed char instance 1098 | */ 1099 | llvm::Value * NChar::codeGen(CodeGenContext & context) 1100 | { 1101 | return llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(8, value, true)); 1102 | } 1103 | 1104 | /** 1105 | Generates code for bool values. 1106 | 1107 | @param CodeGenContext & context -- reference to the context of the operator statement 1108 | @return llvm::Value * -- Pointer to the code generated as a result of the bool instance 1109 | */ 1110 | llvm::Value * NBool::codeGen(CodeGenContext & context) 1111 | { 1112 | char v = value ? 1 : 0; 1113 | return llvm::ConstantInt::get(llvm::getGlobalContext(), llvm::APInt(1, v, true)); 1114 | } 1115 | --------------------------------------------------------------------------------