Repository: william01110111/Pinecone Branch: master Commit: a4550af6d58f Files: 111 Total size: 324.4 KB Directory structure: gitextract_dt6obtpb/ ├── .gitignore ├── LICENSE ├── Makefile ├── _config.yml ├── changelog.md ├── codeblocks/ │ ├── Pinecone.cbp │ └── readme.md ├── examples/ │ ├── brainfuck.pn │ ├── fizzBuzz.pn │ ├── hello_world.pn │ ├── historical/ │ │ └── snake_original.pn │ ├── incomplete/ │ │ └── hashmap.pn │ ├── morse.pn │ ├── queue.pn │ ├── readme.md │ ├── simple_demo.pn │ └── snake_new.pn ├── h/ │ ├── Action.h │ ├── AllOperators.h │ ├── AstNode.h │ ├── CppProgram.h │ ├── ErrorHandler.h │ ├── Namespace.h │ ├── Operator.h │ ├── PineconeProgram.h │ ├── SourceFile.h │ ├── StackFrame.h │ ├── Token.h │ ├── Type.h │ ├── VERSION.h │ ├── msclStringFuncs.h │ └── utils/ │ ├── fileUtils.h │ ├── stringArray.h │ ├── stringDrawing.h │ ├── stringNumConversion.h │ └── stringUtils.h ├── other/ │ ├── pinecone.pn │ ├── pinecone_concept.txt │ ├── readme.md │ └── user_testing.txt ├── readme.md ├── repl/ │ ├── readme.md │ └── repl.sh ├── src/ │ ├── Actions/ │ │ ├── Action.cpp │ │ ├── BoolOpAction.cpp │ │ ├── BranchAction.cpp │ │ ├── FunctionAction.cpp │ │ ├── IfAction.cpp │ │ ├── ListAction.cpp │ │ ├── LoopAction.cpp │ │ ├── MakeTupleAction.cpp │ │ ├── TypeAction.cpp │ │ └── VarAction.cpp │ ├── AllOperators.cpp │ ├── AstNode.cpp │ ├── CppProgram.cpp │ ├── ErrorHandler.cpp │ ├── Lexer.cpp │ ├── Namespace.cpp │ ├── Parser.cpp │ ├── PineconeProgram.cpp │ ├── PineconeStdLib.cpp │ ├── ResolveLiteral.cpp │ ├── SourceFile.cpp │ ├── StackFrame.cpp │ ├── Token.cpp │ ├── Type.cpp │ ├── main.cpp │ ├── msclStringFuncs.cpp │ └── utils/ │ ├── fileUtils.cpp │ ├── stringArray.cpp │ ├── stringDrawing.cpp │ ├── stringNumConversion.cpp │ └── stringUtils.cpp ├── tests/ │ ├── integration/ │ │ ├── brainfuck.pn │ │ ├── morse.pn │ │ └── queue.pn │ ├── readme.md │ ├── regression/ │ │ ├── bool_short_circuit.pn │ │ ├── cpp_bad_escape_seq.pn │ │ ├── cpp_global_and_local_same_name.pn │ │ ├── error_on_nonexistant_file.pn │ │ ├── function_with_one_named_arg.pn │ │ ├── if_as_ternary.pn │ │ ├── multiple_left_inputs.pn │ │ ├── negative_literal.pn │ │ └── no_newline_at_end.pn │ ├── run_tests.pn │ ├── unfixed/ │ │ └── backslash_in_block_comment.pn │ └── unit/ │ ├── conditionals.pn │ ├── constants.pn │ ├── destroyers_and_copiers.pn │ ├── funcs.pn │ ├── generate_windows_line_endings.sh │ ├── loops.pn │ ├── operators.pn │ ├── order_of_ops.pn │ ├── strings.pn │ ├── type_info.pn │ ├── vars.pn │ ├── whatevs.pn │ └── windows_line_endings.pn └── tutorials/ ├── 0_setting_up.md ├── 1_basic_concepts.md ├── 2_control_flow.md ├── 3_strings_and_input.md ├── 4_structures_and_functions.md ├── 5_whatevs.md ├── 6_transpiling_to_cpp.md ├── 7_temporary_hacks.md └── index.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # object files *.o # codeblocks project files *.layout *.depend *.cscope_file_list # Valgrind *cachegrind.out* *ValgrindOut.xml # executables PineconeDbg PineconeBin Pinecone pinecone #KDevelop .kdev4 # stupid OSX stuff *.DS_Store # repl temp file repl/tmp.pn ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Sophie Winter Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ # I'm totally aware how shitty this makefile is # I use the IDE Code::Blocks; this is only for people who don't have that # I'll write a proper makefile at some point build: g++ -Wall -std=c++11 -O2 src/*.cpp src/Actions/*.cpp src/utils/*.cpp -o pinecone ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-hacker ================================================ FILE: changelog.md ================================================ # Pinecone Changelog This file documents all major changes to the Pinecone langauge. Every new version gets a number of minor bug fixes, so they are not included ### v0.5.1 (April 20, 2017) * Fixed silent error when file fails to load * Fixed known bug with functions that take one named argument * Fixed known bug that prevented `?` being useed in a ternary expression * Improved output of testing script * Internal codebase cleanup ## v0.5.0 (April 14, 2017) * Made `&&` and `||` use short circuiting * Improved testing system * Added support for Windows * __Added Whatev type__ for compile time dynamic typing and implicit function return type ## v0.4.0 (Mar 26, 2017) * Added command line options for transpileing * __Fully implemented transpiler to C++__ * Disabled implicit conversion between two structs (if neither is an anonymous tuple) * Added `!=` operator * Wrote more example programs including a brainfuck interpreter * Fixed bug that prevented accessing multiple layers of structures with dot ('struct.data.func: arg' now works) * Added a wide range of unit tests and integration tests * Added escape character support to Strings * Added support for running shell commands * Added explicit conversion of primitive types to Strings * Improved tutorial navigation ## v0.3.0 (Jan 18, 2017) * __Added string literal support__ * Added various functions and operators for strings * Fixed globals ### v0.2.1 (Jan 16, 2017) * Fixed bug introduced in v0.2.0 with functions * Fixed bug with line numbers in error messages ## v0.2.0 (Jan 16, 2017) * Added file import feature * Flipped open and close block comments * Removed debugging info by default * Added command line args ## v0.1.0 (Jan 15, 2017) * __Created Pinecone__ ## v0.0.0 * Pinecone did not exist ================================================ FILE: codeblocks/Pinecone.cbp ================================================ ================================================ FILE: codeblocks/readme.md ================================================ I use Code::Blocks as my primary IDE, and so it is convenient for me to keep a Code::Blocks project in this repo. All Code::Blocks specific stuff should be in this directory. ================================================ FILE: examples/brainfuck.pn ================================================ // This is an interpreter for the esoteric programming language brainfuck \\ data: IntArray: 40000 offset: 0 error: fls inText: "" j: 0 | j+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." processSection: code, 0, tru error ? ( print: "program aborted due to error" print: "position at exit:" print: offset ) # runs a loop of bf code, returns when done processSection :: {src: String, start: Int, isTopLevel: Bool}: ( i: (in.start) quit: fls !quit && !error @ ( c: in.src.sub: i, i+1 c = "<" ? ( offset: offset-1 offset < 0 ? ( print: "you went too far left" error: tru ) ) | c = ">" ? ( offset: offset+1 offset >= data.len ? ( print: "you went too far right" error: tru ) ) | c = "+" ? ( data.set: offset, (data.get: offset)+1 ) | c = "-" ? ( data.set: offset, (data.get: offset)-1 ) | c = "." ? ( printc: data.get: offset ) | c = "," ? ( data.set: offset, inputc ) | c = "[" ? ( processSection: in.src, i+1, fls i: skipBrace: in.src, i ) | c = "]" ? ( in.isTopLevel ? ( print: "']' without matching '['" error: tru )|( (Bool: data.get: offset) ? ( i: in.start-1 )|( quit: tru ) ) ) i: i+1 i >= in.src.len ? ( in.isTopLevel ? ( quit: tru )|( print: "'[' without matching ']'" error: tru ) ) ) ) skipBrace :: {src: String, i: Int} -> {Int}: ( i: in.i (in.src.sub: i, i+1) != "[" ? ( print: "skipBrace called on character other then '['" error: tru )|( i: i+1 !error && (in.src.sub: i, i+1) != "]" @ ( i: i+1 i >= in.src.len ? ( print: "'[' without matching ']'" error: tru ) ) ) i ) #>++++++++[-<+++++++++>]<.>>+>-[+]++>++>+++[>[->+++<<+++>]<<]>-----.>->+++..+++.>-.<<+[>[+>+]>>]<--------------.>>.+++.------.--------.>+.>+. ================================================ FILE: examples/fizzBuzz.pn ================================================ # FizzBuzz # call the function defined below fizzBuzz: 1, 20 # define the FizzBuzz function fizzBuzz :: {start: Int, end: Int}: ( # loop i from start to end i: in.start | i <= in.end | i: i+1 @ ( # use conditionals to print the right thing i % 3 = 0 && i % 5 = 0 ? print: "FizzBuzz" | i % 3 = 0 ? print: "Fizz" | i % 5 = 0 ? print: "Buzz" | print: i ) ) ================================================ FILE: examples/hello_world.pn ================================================ print: "Hello World!" ================================================ FILE: examples/historical/snake_original.pn ================================================ # this was written on Jan 12th, 2016. # this is the first complex pinecone program ever wretten. # type the numbers 1, 2, 3 and 4 followed by enter to move. # because of how early it was wretten, it doesn't use modern constructs, like functions. w :: 16 h :: 8 meChar :: 79 trailChar :: 43 # init locX: w/2 locY: h/2 quit: fls ary: IntArray: w*h y: 0 | y=w? locX: w-1 locY<0? locY: 0 locY>=h? locY: h-1 (ary.get: locY*w+locX)=trailChar ? ( quit: tru )|( ary.set: locY*w+locX, trailChar # display board i: 0 | i<6 | i: i+1 @ print printc: 32 x: 0 | x {Hashmap}: ( size: 10 used: (IntArray: size) i: 0 | i {Hashmap}: ( used: (IntArray: size) i: 0 | i {Int}: me.keys.len String :: {Hashmap} -> {String}: ( out: "hashmap:\n" i: 0 | i {String}: ( out: "" i: 0 | i {String}: ( out: "" found: fls i: 0 | i {Queue}: ( (IntArray: 1), 0, 0 ) resize :: {Int}: ( newArray: IntArray: in i: 0 | i= q.array.len ? resize: q.array.len*2 q: q.array, q.offset, q.len+1 tmp: q.array tmp.set: (q.offset+q.len-1)%q.array.len, in ) dequeue :: {} -> {Int}: ( tmp: q.array out: tmp.get: q.offset%q.array.len q: q.array, q.offset+1, q.len-1 q.len < q.array.len/3+1 ? resize: q.array.len/3+1 out ) peek :: {}: ( tmp: q.array tmp.get: q.offset%q.array.len ) print :: {Queue}: ( print: "queue {" i: 0 | i "+out | i = (in.offset+in.len-1)%in.array.len ? out: " -| "+out | out: " "+out print: out ) print: "}" print \\ ) ================================================ FILE: examples/readme.md ================================================ These are some examples of working Pinecone source code ================================================ FILE: examples/simple_demo.pn ================================================ // This file demonstrates the basic syntax constructs of Pinecone to run, simply run command path/to/pinecone/executable simple_demo.pn \\ # that was a multi line comment. this is a single line comment # create a variable a: 12 # deduces the type as Int # print the value in the variable print: a print # print an empty line print: "if/then" b: 7 a = 9 ? ( print: 1 )|( print: 2 ) b>3 ? ( print: 3 ) print print: "while loop" i: 12 i>3 @ ( print: i i: i-1 ) print print: "for loop" i: 0 | i<10 | i: i+1 @ ( print: i ) print print: "functions" print: func: 7 func :: {Int} -> {Dub}: ( (Dub: in)/2.0 ) print print: "Fibonacci" b: 0 a: 1 a<100 @ ( print: a tmp: a a: a+b b: tmp ) print print: "Recursive Fibonacci" fib :: {Int} -> {Int}: ( out: Int in <= 1 ? out: 1 | out: (fib: in - 1) + (fib: in - 2) out ) print: fib: 8 print print: "compile time constants" # these can be declared in any order print: y x :: 10 y :: x+z z :: 4 ================================================ FILE: examples/snake_new.pn ================================================ # this is an updated version of the original Pinecone snake game # use WASD to move w :: 16 h :: 8 meChar :: 79 trailChar :: 43 # init locX: w/2 locY: h/2 quit: fls ary: IntArray: w*h clearBoard # game loop !quit @ ( # update locX<0? locX: 0 locX>=w? locX: w-1 locY<0? locY: 0 locY>=h? locY: h-1 (ary.get: locY*w+locX)=trailChar ? ( quit: tru )|( drawBoard ary.set: locY*w+locX, trailChar # get input valid: fls !valid @ ( dir: "use WASD to move, then press return: ".input valid: tru dir="s" ? locY: locY+1 | dir="d" ? locX: locX+1 | dir="w" ? locY: locY-1 | dir="a" ? locX: locX-1 | valid: fls ) ) ) clearBoard :: {}: ( y: 0 | y #include #include #include #include "utils/stringDrawing.h" #include // malloc() and free() on some systems using std::shared_ptr; using std::unique_ptr; using std::to_string; using std::function; using std::vector; class ActionData; extern shared_ptr voidAction; class ActionData { public: ActionData(Type returnTypeIn, Type inLeftTypeIn, Type inRightTypeIn); virtual ~ActionData() {} void setDescription(string in) {description=in;} //void setText(string in) {text=in;} //string getText() {return text;} string toString(); string getTypesString(); virtual bool isFunction() {return false;} Type& getReturnType() {return returnType;} Type& getInLeftType() {return inLeftType;} Type& getInRightType() {return inRightType;} //void* execute(void* inLeft, void* inRight); virtual string getDescription() {return description;} virtual void* execute(void* inLeft, void* inRight)=0; //virtual string getCSource(string inLeft="", string inRight="")=0; virtual void addToProg(shared_ptr inLeft, shared_ptr inRight, CppProgram* prog) { prog->comment("action '"+getDescription()+"' to cpp code not yet implemented"); } void addToProg(CppProgram* prog) {addToProg(voidAction, voidAction, prog);} // void addToProg(Action inLeft, Action inRight, CppProgram* prog) string nameHint=""; protected: //string text; Type returnType; Type inLeftType; Type inRightType; string description; //virtual DataElem * privateExecute(DataElem * inLeft, DataElem * inRight)=0; }; typedef shared_ptr Action; class AstNodeBase; //Action lambdaAction(Type returnTypeIn, function lambdaIn, Type inLeftTypeIn, Type inRightTypeIn, string textIn); Action lambdaAction(Type inLeftTypeIn, Type inRightTypeIn, Type returnTypeIn, function lambdaIn, function addCppToProg, string textIn); Action createNewVoidAction(); Action branchAction(Action leftInputIn, Action actionIn, Action rightInputIn); Action functionAction(Action actionIn, shared_ptr stackFameIn); Action functionAction(unique_ptr nodeIn, Type returnTypeIn, shared_ptr stackFameIn); Action andAction(Action firstActionIn, Action secondActionIn); Action orAction(Action firstActionIn, Action secondActionIn); Action ifAction(Action conditionIn, Action ifActionIn); Action ifElseAction(Action conditionIn, Action ifActionIn, Action elseAction); Action listAction(const std::vector& actionsIn, const std::vector& destroyersIn); Action loopAction(Action conditionIn, Action loopActionIn); Action loopAction(Action conditionIn, Action endActionIn, Action loopActionIn); Action makeTupleAction(const std::vector& sourceActionsIn); //Action getElemFromTupleAction(Action source, string name); Action getElemFromTupleAction(Type source, string name); Action cppTupleCastAction(Action actionIn, Type returnType); Action varGetAction(size_t in, Type typeIn, string idIn); Action varSetAction(size_t in, Type typeIn, string idIn); Action globalGetAction(size_t in, Type typeIn, string idIn); Action globalSetAction(size_t in, Type typeIn, string idIn); class NamespaceData; Action constGetAction(const void* in, Type typeIn, string idIn, shared_ptr ns); Action typeGetAction(Type typeIn); ================================================ FILE: h/AllOperators.h ================================================ #pragma once #include using std::vector; #include using std::unordered_map; #include "Operator.h" class AllOperators { public: // sets ops to a new instance static void init(); // sets out to the operators (0, 1 or more) for the given text void get(string text, vector& out); /* #define DECLARE_OP(name, text, left, right, overload)\ const Operator name{new OperatorData(text, left, right, overload)}; #define ALL_OPS \ DECLARE_OP( loop, "@", 5, 5, false ); \ DECLARE_OP( ifOp, "?", 5, 5, false ); \ DECLARE_OP( pipe, "|", 10, 10, false ); \ DECLARE_OP( plus, "+", 20, 21, true ); \ DECLARE_OP( minus, "-", 20, 21, true ); \ DECLARE_OP( multiply, "*", 30, 31, true ); \ DECLARE_OP( divide, "/", 30, 31, true ); \ DECLARE_OP( equal, "=", 40, 41, true ); \ DECLARE_OP( greater, ">", 40, 41, true ); \ DECLARE_OP( less, "<", 40, 41, true ); \ DECLARE_OP( comma, ",", 60, 61, false ); \ DECLARE_OP( dot, ".", 70, 71, false ); \ DECLARE_OP( doubleColon,"::", 13, 12, false ); \ DECLARE_OP( colon, ":", 15, 14, false ); \ DECLARE_OP( openPeren, "(", 0, 100, false ); \ DECLARE_OP( closePeren, ")", 100, 0, false ); \ DECLARE_OP( openSqBrac, "[", 0, 100, false ); \ DECLARE_OP( closeSqBrac,"]", 100, 0, false ); \ DECLARE_OP( openCrBrac, "{", 0, 100, false ); \ DECLARE_OP( closeCrBrac,"}", 100, 0, false ); \ */ #define DECLARE_OP(name, text, prece, input, overload)\ const Operator name{new OperatorData(text, prece, input, overload)}; #define ALL_OPS \ DECLARE_OP( loop, "@", 5, OperatorData::BOTH, false ); \ DECLARE_OP( ifOp, "?", 6, OperatorData::BOTH, false ); \ DECLARE_OP( pipe, "|", 6, OperatorData::BOTH, false ); \ DECLARE_OP( colon, ":", 24, OperatorData::BOTH, false ); \ DECLARE_OP( doubleColon,"::", 24, OperatorData::BOTH, false ); \ DECLARE_OP( comma, ",", 35, OperatorData::BOTH, false ); \ DECLARE_OP( orOp, "||", 36, OperatorData::BOTH, false ); \ DECLARE_OP( andOp, "&&", 38, OperatorData::BOTH, false ); \ DECLARE_OP( equal, "=", 40, OperatorData::BOTH, true ); \ DECLARE_OP( notEqual, "!=", 40, OperatorData::BOTH, false ); \ DECLARE_OP( greater, ">", 50, OperatorData::BOTH, true ); \ DECLARE_OP( less, "<", 50, OperatorData::BOTH, true ); \ DECLARE_OP( greaterEq, ">=", 50, OperatorData::BOTH, true ); \ DECLARE_OP( lessEq, "<=", 50, OperatorData::BOTH, true ); \ DECLARE_OP( plus, "+", 61, OperatorData::BOTH, true ); \ DECLARE_OP( minus, "-", 61, OperatorData::BOTH, true ); \ DECLARE_OP( multiply, "*", 71, OperatorData::BOTH, true ); \ DECLARE_OP( divide, "/", 71, OperatorData::BOTH, true ); \ DECLARE_OP( mod, "%", 70, OperatorData::BOTH, true ); \ DECLARE_OP( notOp, "!", 74, OperatorData::RIGHT, true ); \ DECLARE_OP( plusPlus, "++", 75, OperatorData::LEFT, false ); \ DECLARE_OP( minusMinus, "--", 75, OperatorData::LEFT, false ); \ DECLARE_OP( dot, ".", 81, OperatorData::BOTH, false ); \ DECLARE_OP( rightArrow, "->", 83, OperatorData::BOTH, false ); \ DECLARE_OP( import, "==>", 90, OperatorData::RIGHT, false ); \ DECLARE_OP( openPeren, "(", 100, OperatorData::RIGHT, false ); \ DECLARE_OP( closePeren, ")", 99, OperatorData::LEFT, false ); \ DECLARE_OP( openSqBrac, "[", 100, OperatorData::BOTH, false ); \ DECLARE_OP( closeSqBrac,"]", 99, OperatorData::LEFT, false ); \ DECLARE_OP( openCrBrac, "{", 100, OperatorData::RIGHT, false ); \ DECLARE_OP( closeCrBrac,"}", 99, OperatorData::LEFT, false ); \ ALL_OPS; // for read only, dont screw with this unordered_map& getOpsMap() {return opsMap;} // returns if the given operator is an opening bracket or peren bool isOpenBrac(Operator op); bool isCloseBrac(Operator op); private: // is only called in init AllOperators(); // used internally by the constructor void putOpInMap(Operator op); // a hash map of all the operators unordered_map opsMap; }; // the single instance of this class, starts out as nullptr but chages to a real instance in AllOperators::init //extern shared_ptr ops; extern AllOperators* ops; ================================================ FILE: h/AstNode.h ================================================ #pragma once #include "Token.h" #include "Action.h" #include "ErrorHandler.h" class NamespaceData; typedef shared_ptr Namespace; #include using std::vector; class AstNodeBase; typedef unique_ptr AstNode; AstNode astNodeFromTokens(const vector& tokens, int left, int right); class AstNodeBase { public: virtual ~AstNodeBase() = default; void setInput(Namespace nsIn, bool dynamicIn, Type left, Type right) { if (inputHasBeenSet) { throw PineconeError("tried to set input on an AST node '"+getString()+"' more then once", INTERNAL_ERROR, getToken()); } inputHasBeenSet=true; ns=nsIn; dynamic=dynamicIn; inLeftType=left; inRightType=right; } virtual bool isVoid() {return false;} virtual bool isType() {return false;} virtual bool isFunctionWithOutput() {return false;} /* Type getInLeftType() { if (!inputHasBeenSet) throw PineconeError("tried to get inLeftType before input was set", INTERNAL_ERROR, getToken()); return inLeftType; } Type getInRightType() { if (!inputHasBeenSet) throw PineconeError("tried to get inRightType before input was set", INTERNAL_ERROR, getToken()); return inRightType; } */ virtual string getString()=0; virtual AstNode makeCopy(bool copyCache)=0; // if copyCache is false, input and actions will not be copied virtual bool canBeWhatev() {return false;} // returns nullptr if it can't do it virtual AstNode makeCopyWithSpecificTypes(Type leftInType, Type rightInType) {return nullptr;} Type getReturnType() { if (!returnType) { if (!inputHasBeenSet) { throw PineconeError("tried to get return type from AST node when input had not been set", INTERNAL_ERROR, getToken()); } resolveReturnType(); } if (!returnType) { throw PineconeError("AST node "+getString()+"failed to supply a return type", INTERNAL_ERROR); } return returnType; } Action getAction() { if (!action) { if (!ns) { throw PineconeError("tried to get action from AST node when input had not been set", INTERNAL_ERROR, getToken()); } resolveAction(); if (!nameHint.empty()) action->nameHint=nameHint; } return action; } void dealWithConstants() { if (!ns) { throw PineconeError("tried to deal with constants before input was set", INTERNAL_ERROR, getToken()); } resolveConstant(); } // primarily used for error throwing, can return null virtual Token getToken()=0; string nameHint=""; virtual void nameHintSet() {} protected: AstNodeBase() {} void copyToNode(AstNodeBase* other, bool copyCache); virtual void resolveReturnType() { returnType=getAction()->getReturnType(); } virtual void resolveAction()=0; virtual void resolveConstant() {}; Type inLeftType=nullptr, inRightType=nullptr; Action action=nullptr; Type returnType=nullptr; bool dynamic=false; Namespace ns=nullptr; bool inputHasBeenSet=false; }; class AstVoid: public AstNodeBase { public: static unique_ptr make() {return unique_ptr(new AstVoid);} bool isVoid() {return true;} string getString() {return "void node";} AstNode makeCopy(bool copyCache) { auto out=new AstVoid; copyToNode(out, copyCache); return AstNode(out); } void resolveReturnType() {returnType=Void;} void resolveAction() { if (!inLeftType->isVoid() || !inRightType->isVoid()) { throw PineconeError("AstVoid given non void input", INTERNAL_ERROR, getToken()); } action=voidAction; } Token getToken() {return nullptr;} }; //extern AstNode astVoid; class AstList: public AstNodeBase { public: // make a new instance of this type of node static unique_ptr make(vector& in) { unique_ptr node(new AstList); node->nodes=move(in); return node; } string getString(); AstNode makeCopy(bool copyCache) { auto out=new AstList; copyToNode(out, copyCache); for (int i=0; i<(int)nodes.size(); i++) { out->nodes.push_back(nodes[i]->makeCopy(copyCache)); } return AstNode(out); } //void resolveReturnType(); void resolveAction(); Token getToken() {return nodes.empty()?nullptr:nodes[0]->getToken();} private: // the list of sub nodes vector nodes; }; class AstConstExpression; class AstToken: public AstNodeBase { public: static unique_ptr make(Token tokenIn) { unique_ptr node(new AstToken); node->token=tokenIn; return node; } string getString(); AstNode makeCopy(bool copyCache) { auto out=new AstToken; copyToNode(out, copyCache); out->token=token; return AstNode(out); } void resolveAction(); Token getToken() {return token;} private: friend AstConstExpression; Token token=nullptr; }; class AstFuncBody: public AstNodeBase { public: static AstNode make(AstNode leftTypeIn, AstNode rightTypeIn, AstNode returnTypeIn, AstNode bodyIn) { auto node=new AstFuncBody(); if (!leftTypeIn->isType() || !rightTypeIn->isType() || !returnTypeIn->isType()) { throw PineconeError("AstFuncBody made with function input nodes that are not types", INTERNAL_ERROR); } node->leftTypeNode=move(leftTypeIn); node->rightTypeNode=move(rightTypeIn); node->returnTypeNode=move(returnTypeIn); node->bodyNode=move(bodyIn); return AstNode(node); } string getString(); AstNode makeCopy(bool copyCache) { auto out=new AstFuncBody; copyToNode(out, copyCache); out->leftTypeNode=leftTypeNode->makeCopy(copyCache); out->rightTypeNode=rightTypeNode->makeCopy(copyCache); out->returnTypeNode=returnTypeNode->makeCopy(copyCache); out->bodyNode=bodyNode->makeCopy(copyCache); out->typesInputSet=typesInputSet; return AstNode(out); } void resolveAction(); AstNode makeCopyWithSpecificTypes(Type leftInType, Type rightInType); Token getToken() {return bodyNode->getToken();} void setTypesInput() { if (!typesInputSet) { leftTypeNode->setInput(ns, false, Void, Void); rightTypeNode->setInput(ns, false, Void, Void); returnTypeNode->setInput(ns, false, Void, Void); typesInputSet=true; } } bool canBeWhatev() { setTypesInput(); return leftTypeNode->getReturnType()->isWhatev() || rightTypeNode->getReturnType()->isWhatev() || returnTypeNode->getReturnType()->isWhatev(); } AstNode leftTypeNode, rightTypeNode, returnTypeNode, bodyNode; bool typesInputSet=false; }; class AstExpression: public AstNodeBase { public: static unique_ptr make(AstNode leftInIn, AstNode centerIn, AstNode rightInIn) { unique_ptr node(new AstExpression); node->leftIn=move(leftInIn); node->center=move(centerIn); node->rightIn=move(rightInIn); return node; } //bool isType() {return leftIn->isType() || rightIn->isType();} AstNode makeCopy(bool copyCache) { auto out=new AstExpression; copyToNode(out, copyCache); out->leftIn=leftIn->makeCopy(copyCache); out->center=center->makeCopy(copyCache); out->rightIn=rightIn->makeCopy(copyCache); return AstNode(out); } string getString(); void resolveAction(); Token getToken() {return center->getToken();} AstNode leftIn=nullptr, center=nullptr, rightIn=nullptr; }; class AstConstExpression: public AstNodeBase { public: static unique_ptr make(unique_ptr centerIn, AstNode rightInIn) { unique_ptr node(new AstConstExpression); //node->leftIn=move(leftInIn); node->center=move(centerIn); node->rightIn=move(rightInIn); return node; } string getString(); AstNode makeCopy(bool copyCache) { auto out=new AstConstExpression; copyToNode(out, copyCache); out->center=unique_ptr((AstToken*)center->makeCopy(copyCache).release());; out->rightIn=center->makeCopy(copyCache); return AstNode(out); } void resolveConstant(); void resolveAction() {action=voidAction;}; Token getToken() {return center->getToken();} private: //AstNode leftIn=nullptr; unique_ptr center=nullptr; AstNode rightIn=nullptr; }; class AstOpWithInput: public AstNodeBase { public: static unique_ptr make(vector& leftInIn, Token tokenIn, vector& rightInIn) { unique_ptr node(new AstOpWithInput); node->leftIn=move(leftInIn); node->token=tokenIn; node->rightIn=move(rightInIn); return node; } bool isFunctionWithOutput(); string getString(); AstNode makeCopy(bool copyCache) { auto out=new AstOpWithInput; copyToNode(out, copyCache); out->token=token; for (int i=0; i<(int)leftIn.size(); i++) { out->leftIn.push_back(leftIn[i]->makeCopy(copyCache)); } for (int i=0; i<(int)rightIn.size(); i++) { out->rightIn.push_back(rightIn[i]->makeCopy(copyCache)); } return AstNode(out); } void resolveAction(); Token getToken() {return token;} Token token=nullptr; vector leftIn, rightIn; }; class AstTuple: public AstNodeBase { public: // make a new instance of this type of node static unique_ptr make(vector& in) { unique_ptr node(new AstTuple); node->nodes=move(in); return node; } string getString(); AstNode makeCopy(bool copyCache) { auto out=new AstTuple; copyToNode(out, copyCache); for (int i=0; i<(int)nodes.size(); i++) { out->nodes.push_back(nodes[i]->makeCopy(copyCache)); } return AstNode(out); } //void resolveReturnType(); void resolveAction(); Token getToken() {return nodes.empty()?nullptr:nodes[0]->getToken();} private: vector nodes; }; class AstType: public AstNodeBase { public: bool isType() {return true;} void resolveAction() { throw PineconeError("AstType::resolveAction called, which it shouldn't have been", INTERNAL_ERROR, getToken()); } }; class AstTypeType: public AstType { public: static unique_ptr make(Type typeIn) { unique_ptr node(new AstTypeType); node->returnTypeNotMeta=typeIn; return node; } string getString() { return returnType->getString(); } AstNode makeCopy(bool copyCache) { auto out=new AstTypeType; copyToNode(out, copyCache); out->returnType=returnType; return AstNode(out); } void resolveReturnType() { returnType=returnTypeNotMeta->getMeta(); } void nameHintSet() { if (!nameHint.empty() && returnTypeNotMeta->nameHint.empty()) returnTypeNotMeta->nameHint=nameHint; } Token getToken() {return nullptr;} private: Type returnTypeNotMeta; }; class AstVoidType: public AstType { public: static unique_ptr make() { unique_ptr node(new AstVoidType); return node; } string getString() {return "{}";} AstNode makeCopy(bool copyCache) { auto out=new AstVoidType; copyToNode(out, copyCache); return AstNode(out); } void resolveReturnType() { returnType=Void->getMeta(); } Token getToken() {return nullptr;} private: }; class AstTokenType: public AstType { public: static unique_ptr make(Token tokenIn) { unique_ptr node(new AstTokenType); node->token=tokenIn; return node; } string getString(); AstNode makeCopy(bool copyCache) { auto out=new AstTokenType; copyToNode(out, copyCache); out->token=token; return AstNode(out); } void resolveReturnType(); Token getToken() {return token;} private: Token token=nullptr; }; class AstTupleType: public AstType { public: struct NamedType { Token name; // can be null unique_ptr type; }; static unique_ptr make(vector& in) { unique_ptr node(new AstTupleType); node->subTypes=move(in); return node; } string getString(); AstNode makeCopy(bool copyCache) { auto out=new AstTupleType; copyToNode(out, copyCache); for (int i=0; i<(int)subTypes.size(); i++) { out->subTypes.push_back({subTypes[i].name, unique_ptr((AstType*)subTypes[i].type->makeCopy(copyCache).release())}); } return AstNode(out); } void resolveReturnType(); Token getToken() {return subTypes.empty()?nullptr:subTypes[0].name;} private: vector subTypes; }; class AstActionWrapper: public AstNodeBase { public: static unique_ptr make(Action actionIn) { auto out = unique_ptr(new AstActionWrapper); out->inLeftType=actionIn->getInLeftType(); out->inRightType=actionIn->getInRightType(); out->returnType=actionIn->getReturnType(); out->action=actionIn; out->dynamic=true; // shouldn't matter out->ns=nullptr; // shouldn't matter out->inputHasBeenSet=true; return out; } string getString() {return "action wrapper node";} AstNode makeCopy(bool copyCache) { auto out=new AstActionWrapper; copyToNode(out, true); return AstNode(out); } void resolveAction() { throw PineconeError("AstActionWrapper::resolveAction called, which it shouldn't have been", INTERNAL_ERROR); } Token getToken() {return nullptr;} }; class AstWhatevToActionFactory: public AstNodeBase { public: static AstNode make(function lambda) { auto node=new AstWhatevToActionFactory(); node->lambda=lambda; return AstNode(node); } string getString() {return "AstWhatevToActionFactory";} AstNode makeCopy(bool copyCache) { auto out=new AstWhatevToActionFactory; copyToNode(out, copyCache); out->lambda=lambda; return AstNode(out); } void resolveAction() {throw PineconeError("AstWhatevToActionFactory::resolveAction called, wich should never happen", INTERNAL_ERROR);} AstNode makeCopyWithSpecificTypes(Type leftInType, Type rightInType) { auto action=lambda(leftInType, rightInType); if (action) return AstActionWrapper::make(action); else return nullptr; } Token getToken() {return nullptr;} bool canBeWhatev() {return true;} private: function lambda; }; ================================================ FILE: h/CppProgram.h ================================================ #pragma once #include "Type.h" #include #include // Pinecone names that are hardcoded should start with '-' so that collisions can't happen with input code /* naming conventions for hardcoded Pineocne name prefixes these go in frount of any names that are hardcoded for the transpiler, since they are not valid identifier cars they can not collide with names from pn source and they will be removed before they get to C++ source global funcs: $ global vars: * local vars: - types: surounded by {} */ class CppNameContainer { public: static shared_ptr makeRoot(); shared_ptr makeChild(); void addPn(const string& pn, const string& cppNameHint="<- the value of that pn string please"); // will throw an error if pnName already exists void reserveCpp(const string& cpp, bool ignoreCollisions=false); bool hasPn(const string& pn); string getCpp(const string& pn); // will throw an error if the Pinecone name doesn't exist CppNameContainer* getParent() {return parent;} private: bool hasPnMe(const string& pn); CppNameContainer(); bool hasCpp(const string& cpp); bool hasCppMe(const string& cpp); bool hasCppUp(const string& cpp); bool hasCppDown(const string& cpp); std::unordered_set cppSet; std::map pnToCppMap; CppNameContainer* parent=nullptr; vector> children; }; class CppFuncBase { public: CppFuncBase(string prototypeIn, shared_ptr myNames, bool returnsValIn); void code(const string& in); void name(const string& in); // converts a Pinecone name to a posibly different C++ name void line(const string& in); void endln(); void comment(const string& in); void pushExpr(); void popExpr(); void pushBlock(); void popBlock(); string pnToCpp(const string& in); int getExprLevel() {return exprLevel;} bool getIfFreshLine() {return freshLine;} int getBlockLevel() {return blockLevel;} bool getIfReturnsVal() {return returnsVal;} string getSource() {return source;} string getPrototype() {return prototype;} private: string indent="\t"; bool freshLine=true; int blockLevel=0; int exprLevel=0; string varDeclareSource; string source; string prototype; bool returnsVal=false; bool fakeStartBlock=false; vector> namespaceStack; friend CppProgram; }; typedef shared_ptr CppFunc; class CppProgram { public: CppProgram(); void code(const string& in) {activeFunc->code(in);} void name(const string& in) {activeFunc->name(in);} void line(const string& in) {activeFunc->line(in);} void endln() {activeFunc->endln();} void comment(const string& in) {activeFunc->comment(in);} void pushExpr() {activeFunc->pushExpr();} void popExpr() {activeFunc->popExpr();} void pushBlock() {activeFunc->pushBlock();} void popBlock() {activeFunc->popBlock();} string pnToCpp(const string& in){return activeFunc->pnToCpp(in);} int getExprLevel() {return activeFunc->getExprLevel();} int getBlockLevel() {return activeFunc->getBlockLevel();} int getIfReturnsVal() {return activeFunc->getIfReturnsVal();} void setup(); string getTypeCode(Type in); void declareVar(const string& nameIn, Type typeIn, string initialValue=""); void declareGlobal(const string& nameIn, Type typeIn, string initialValue=""); void addHeadCode(const string& code); bool hasFunc(const string& name); void addFunc(const string& name, vector> args, string returnType, string contents); void pushFunc(const string& name, const string& cppNameHint, Type leftIn, Type rightIn, Type returnType); void pushFunc(const string& name, Type leftIn, Type rightIn, Type returnType) {pushFunc(name, name, leftIn, rightIn, returnType);} //void pushFunc(const string&, vector args, Type returnType); void popFunc(); bool isMain() {return funcStack.size()==1;} string getCppCode(); shared_ptr getGlobalNames() {return globalNames;}; private: string indent="\t"; string globalTopCode; string globalIncludesCode; string globalVarCode; string globalTypesCode; CppFunc activeFunc; vector funcStack; std::map funcs; shared_ptr globalNames; }; void addToProgPnStr(CppProgram * prog); ================================================ FILE: h/ErrorHandler.h ================================================ #pragma once #include "Token.h" #include using std::string; #define FUNC string(__FUNCTION__) //#define FUNC string(__PRETTY_FUNCTION__) //like __FUNCTION__, but adds class name enum ErrorPriority { SOURCE_ERROR, SOURCE_WARNING, JSYK, INTERNAL_ERROR, RUNTIME_ERROR }; class ErrorHandler { public: static string priorityToStr(ErrorPriority in); void log(string msg, ErrorPriority priority, Token token=nullptr); void msg(string in); bool getIfErrorLogged() {return errorHasBeenLogged;} private: bool errorHasBeenLogged=false; }; extern ErrorHandler error; class PineconeError { public: PineconeError(string msgIn, ErrorPriority priorityIn, Token tokenIn=nullptr); void log(); private: string msg; ErrorPriority priority; Token token; }; //typedef shared_ptr Err; //Err makeError(string msgIn, ErrorPriority priorityIn, Token tokenIn=nullptr) {return Err(new PineconeError(msgIn, priorityIn, tokenIn));} ================================================ FILE: h/Namespace.h ================================================ #pragma once #include "Type.h" #include "Action.h" #include "Token.h" #include "Operator.h" #include "AstNode.h" #include using std::unordered_map; #include using std::vector; class StackFrame; class NamespaceData; typedef shared_ptr Namespace; /// has a bunch of hash tables for all the identifiers in a single scope // contains a pointer to a stack frame but there may be many namespaces (due to many scopes) in a single stack frame // for example, a function has a single stack frame but has several if blocks, each with its own NamespaceData // each NamespaceData also has a pointer to its parent, which should only be null for the global standard library // contains a pointer to its parent namespace, which can be null if it is the root // can be created with one of the 'make' functions, not by directly instantiating it class NamespaceData: public std::enable_shared_from_this { public: // makes a namespace with no parents and a new stack frame static Namespace makeRootNamespace(); // makes a child namespace with the same stack frame as this one Namespace makeChild(); // makes a child namespace with a new stack frame Namespace makeChildAndFrame(string nameIn); // returns a string with the complete contents of this namespace string getString(); // retuens a string with this namespace and all it's parents nicely formatted string getStringWithParents(); //Namespace getParent() {return parent;} shared_ptr getStackFrame() {return stackFrame;} void setInput(Type left, Type right); void addNode(AstNode node, string id); // recursivly searches up looking for a type of the given name // returns UnknownType if it cant find the requested type Type getType(string name, bool throwSourceError, Token tokenForError); // returns the destructor of the given type, or nullptr if there isn't one Action getDestroyer(Type type); // returns the copier of the given type, or nullptr if there isn't one Action getCopier(Type type); // returns an action that takes the input types // on error, it will throw a source error if throwSourceError is true. otherwise, it will return nullptr Action getActionForTokenWithInput(Token token, Type left, Type right, bool dynamic, bool throwSourceError, Token tokenForError); vector* getDestroyerActions() {return &destructorActions;} Action wrapInDestroyer(Action in); private: // retrieve all nodes in the given catagories with the given names from this namespace and all its parents void getNodes(vector& out, string text, bool checkActions, bool checkDynamic, bool checkWhatev); void nodesToMatchingActions(vector& out, vector& nodes, Type leftInType, Type rightInType); class IdMap { public: void add(string key, AstNode node); void get(string key, vector& out); private: unordered_map> nodes; }; // add a get and set action for a variable also adds its data to the stack frame Action addVar(Type type, string name); // the only constructor, is private so use a make function instead NamespaceData(Namespace parentIn, shared_ptr stackFrameIn, string nameIn=""); // the name of this namespace, for debugging purposes only string myName; // this namespaces parent shared_ptr parent; // the stack frame this namespace uses // does not have a reference back because there can be many namespaces in one stack frame shared_ptr stackFrame; // contains all actions that can be used anywhere, and don't rely on dynamic content like variables IdMap actions; // contains actions that use runtime data, like variables IdMap dynamicActions; // contains actions that use a whatev type IdMap whatevActions; // contains all types in this namespace IdMap types; // contains destructors for types, the key string is a pointer to the type fed through ptrToUniqueStr with 6 digits //IdMap destructors; // like destructors, but for making copies //IdMap copiers; vector destructorActions; }; ================================================ FILE: h/Operator.h ================================================ #pragma once class TokenData; #include using std::shared_ptr; #include using std::string; class AllOperators; // describes an operator // NOTE: there will only be one instance of OperatorData for +, not one for every overload of + class OperatorData { public: enum InputTaken { LEFT, RIGHT, BOTH, }; string getText() {return text;} int getPrecedence() {return precedence;} bool isOverloadable() {return overloadable;} bool takesLeftInput() {return input==BOTH || input==LEFT;} bool takesRightInput() {return input==BOTH || input==RIGHT;} private: friend AllOperators; OperatorData(string textIn, int precedenceIn, InputTaken inputIn, bool overloadableIn) { text=textIn; precedence=precedenceIn; input=inputIn; overloadable=overloadableIn; } // the text of the operator, generally 1 or 2 characters string text; // the precedence of left and right inputs, if this is 0 then the operator does not take an input on that side // if even then precedence level is parced left to right. if odd then right to left int precedence; // if this operator can be overloaded bool overloadable; // if this operator takes input from the left, right or both InputTaken input; }; typedef shared_ptr Operator; ================================================ FILE: h/PineconeProgram.h ================================================ #pragma once #include "VERSION.h" #include #include #include #include #include using std::max; using std::min; using std::cout; using std::endl; using std::string; using std::to_string; using std::vector; using std::list; #include "msclStringFuncs.h" #include "Namespace.h" #include "StackFrame.h" #include "Token.h" #include "AstNode.h" #include "SourceFile.h" extern vector cmdLineArgs; class Element; class PineconeProgram { public: PineconeProgram(); ~PineconeProgram() {cleanUp();} string getCpp(); void resolveProgram(string inFilename, bool printExtraOutput); Namespace getGlobalActionTable(); void execute(); private: void cleanUp(); private: shared_ptr file=nullptr; //a list of all the tokens in the program vector tokens; //root of the abstract syntax tree AstNode astRoot=nullptr; //root of the action tree Action actionRoot=createNewVoidAction(); vector whitespaceChars, letterChars, digitChars; char singleLineComment; }; ================================================ FILE: h/SourceFile.h ================================================ #pragma once #include using std::string; class SourceFile { public: SourceFile() { filename="[empty_file]"; contents=""; } SourceFile(string filenameIn, bool printOutput); string getFilename() {return filename;} // get the path to the directory this file is in string getDirPath(); const string& getContents() {return contents;} string getBoxedString(); string getLine(int lineNum); inline int size() { return contents.size(); } inline char operator[](int i) { return contents[i]; } inline string substr(size_t start, size_t len) { return contents.substr(start, len); } private: string filename; string contents; }; ================================================ FILE: h/StackFrame.h ================================================ #pragma once #include using std::vector; #include "Type.h" //#include "Action.h" extern void* globalFramePtr; extern void* stackPtr; class StackFrame { public: void addMember(Type in); // set the left and right input types (can only be done once) void setInput(Type left, Type right); size_t getSize() {return frameSize;} Type getLeftInType(); Type getRightInType(); size_t getLeftOffset(); size_t getRightOffset(); private: //Action actions; //must always be functionAction vector members; size_t frameSize=0; bool inputSet=false; int leftInputIndex=-1; int rightInputIndex=-1; size_t leftInputOffset; size_t rightInputOffset; }; ================================================ FILE: h/Token.h ================================================ #pragma once #include "Operator.h" #include using std::string; #include using std::vector; class SourceFile; //represents a single token such as an Int literal, an operator, or an identifier //is immutable class TokenData { public: enum Type { WHITESPACE, LINE_END, IDENTIFIER, LITERAL, STRING_LITERAL, OPERATOR, LINE_COMMENT, BLOCK_COMMENT, SCOPE, CAST, TUPLE, UNKNOWN }; TokenData(string textIn, shared_ptr fileIn, int lineIn, int charPosIn, Type tokenTypeIn, Operator opIn=Operator(nullptr)) { text=textIn; file=fileIn; line=lineIn; charPos=charPosIn; tokenType=tokenTypeIn; op=opIn; } string getText() const {return text;} shared_ptr getFile() const {return file;} int getLine() const {return line;} int getCharPos() const {return charPos;} TokenData::Type getType() const {return tokenType;} Operator getOp() const {return op;} bool isComment() {return tokenType==LINE_COMMENT || tokenType==BLOCK_COMMENT;} static string typeToString(TokenData::Type in); string getDescription(); string getTypeDescription(); private: string text; shared_ptr file; int line; int charPos; Type tokenType; Operator op; }; typedef shared_ptr Token; Token makeToken(string textIn, shared_ptr fileIn, int lineIn, int charPosIn, TokenData::Type tokenTypeIn, Operator opIn=Operator(nullptr)); Token makeToken(string textIn); string stringFromTokens(const vector& tokens, int left, int right); string tableStringFromTokens(const vector& tokens, string tableName); ================================================ FILE: h/Type.h ================================================ #pragma once #include using std::string; #include using std::vector; #include using std::shared_ptr; using std::unique_ptr; #include using std::memcpy; class TypeBase; typedef shared_ptr Type; const extern Type Unknown; const extern Type Whatev; const extern Type Void; const extern Type Bool; const extern Type Byte; const extern Type Int; const extern Type Dub; extern Type String; class CppProgram; class ActionData; struct NamedType { string name; Type type; }; struct OffsetAndType { size_t offset; Type type; }; class TypeBase: public std::enable_shared_from_this { public: virtual ~TypeBase() = default; ///steps to adding a new type (these may be old): /// 1. Add to the enum /// 2. add it to toString in Type.cpp /// 3. add it to isCreatable in Type.cpp /// 4. if it has a literal, add a class for that in LiteralElement.h /// 5. add the creation of said literal to LiteralElement::makeNew in LiteralElement.cpp /// 6. you may have to add logic to initialProgramPopulation or getElementType to correctly capture the section of source containing your type literal /// 7. add a class to DataElem.h /// 8. add it to makeNewOfType in DataElem.cpp enum PrimitiveType { UNKNOWN , VOID , BYTE, DUB, INT, PTR, BOOL, TUPLE, WHATEV, METATYPE, }; /* type compact forms Void: v Unknown: u Bool: b Byte: y Int: i Dub: d Ptr: Pp_..._pP Tuple: Tt_[size of name]_name_type..._tT Whatev: W Meta: Mm_..._mM */ static Type makeNewVoid(); static Type makeNewPrimitive(PrimitiveType typeIn); static Type makeNewWhatev(); //static Type makeNew(unordered_map, string nameIn); //TypeBase(string nameIn) {name=nameIn;} Type getMeta(); Type getPtr(); virtual Type actuallyIs(Type target); // returns a type with with the whatevs resolved with the target type static string getString(PrimitiveType in); virtual string getString()=0; string getName() {return nameHint.empty()?getCompactString():nameHint;} virtual string getCompactString()=0; // returns a string that is unique, only shared with other types that match this one virtual string getCppLiteral(void * data, CppProgram * prog)=0; virtual bool isCreatable() {return true;}; virtual bool isVoid() {return false;}; virtual bool isWhatev() {return false;}; virtual size_t getSize()=0; //string getName() {return name;} virtual PrimitiveType getType()=0; bool matches(Type other); virtual Type getSubType() {return Void;} virtual OffsetAndType getSubType(string name) {return {0, nullptr};} virtual vector* getAllSubTypes(); // will thow an error if this is not a tuple type // void setNameHint(const string& in) {if (nameHint.empty()) nameHint=in;} // only sets name hint on any type once, after that it fails quietly // string getNameHint string nameHint=""; // not reliable, will often be ""; should only be used as a hint, not depended on protected: virtual bool matchesSameTypeType(Type other)=0; Type ptrToMe=nullptr; }; Type makeTuple(const vector& in, bool isAnonymous); // since types are immutable, this class is an easy way to construct a tuple type class TupleTypeMaker { public: TupleTypeMaker(); void add(string name, Type type); void add(Type type); Type get(bool isAnonymous); private: string getUniqueName(); std::unique_ptr> subTypes; }; ================================================ FILE: h/VERSION.h ================================================ #pragma once // the version of pinecone is vX.Y.Y const int VERSION_X=0; const int VERSION_Y=5; const int VERSION_Z=1; ================================================ FILE: h/msclStringFuncs.h ================================================ #pragma once #include "PineconeProgram.h" #include using std::string; using std::to_string; #include using std::vector; #include using std::string; #include "utils/stringUtils.h" // returns if the substring matches the input // nothing needed for unicode // in: the string to check against // pos: the location in 'in' to line the start of the substring up with // subStr: the string to compare with // returns: if the substring matches the input at the given location bool substringMatches(const string& in, int pos, const string& subStr); // returns the location of a pattern in a string, or -1 if it can't find it // no unicode support yet // in: the string to search // startPos: where to start looking (inclusive) // pattern: the single or multi character pattern to search for, wildcards are not yet supported // returns: the absolute position (not the offset) of the first instance of the pattern in the input, or -1 if it can't find it int searchInString(const string& in, const string& pattern, int startPos=0); // replaces all instances of one substring with another // in: the string to search // searchFor: pattern to replace // replaceWith: new value // returns: in after replace operation string replaceSubstring(const string& in, const string& searchFor, const string& replaceWith); // sets the given array to be the given string sliced up using the given pattern // in: the string to slice // pattern: the pattern used to chop up the string, will not be included in output // out: all the substrings will be appended to this void sliceStringBy(const string& in, const string& pattern, vector& out); // indents a string // in: the string to indent (can be multi line) // indent: the string to use as the indent // returns: the string indented string indentString(const string& in, string indent=" ", int level=1); // returns the original string with the tabs replaced by the correct number of spaces // in: the string to convert // spaceNum: number of spaces per tab // returns: a string with the tabs replaced by spaces string tabsToSpaces(const string& in, int spaceNum=4); // runs tabsToSpaces on an entire array of strings void tabsToSpaces(vector& in); // returns the original string but padded with the given padding string // in: the string to pad // size: the size of the output string // alignment: // 1: left // -1: right // 0: center // pad: the string to use for padding (assumes this string is one char long) // leftCap: a string to put on the left size of the input (inside the padding) that will not get chopped // rightCap: a string to put on the right size of the input (inside the padding) that will not get chopped // returns: the padded string, or the chopped string if the input is shorter then the size string padString(const string& in, int size, int alignment=1, string pad=" ", string leftCap="", string rightCap=""); // returns the specific line (starting at 1) in the string it is sent // in: the (presumably multiline) string // lineNum: the line to return // returns: the specific line (without newlines), or an empty string if that line is invalid string getTextOfLine(const string& in, int lineNum); // takes an array of lines and puts a box around it // in: an array of strings, each which will be on a new line // boxName: the name of the box // lineNum: the offset of line numbers, no line nums will be displayed if -1 // alwaysWidthMax: if to always use the max width // maxWidth: the max width of a line, will be truncated if longer // returns: a string with a box around the content (must be displayed in monospace obviously) string lineListToBoxedString(const vector& in, string boxName="", int lineNum=-1, bool alwaysWidthMax=false, int maxWidth=-1); // puts the contents of a string into a well formatted // in: the input stringz // boxName: the name that will appear at the top of the box // showLineNums: if to show line numbers // alwaysWidthMax: if to always use the max width // maxWidth: the maximum width of the contents of the box (with borders it may be a bit wider), if any line is longer it will be chopped // returns: the boxed string string putStringInBox(const string& in, string boxName="", bool showLineNums=false, bool alwaysWidthMax=false, int maxWidth=-1); // puts the contents of a string into a table, with tabs being verticle seporators and newlines being horizontle ones // in: the input string // tableName: the name that will apear at the top of the table // returns: a string that looks like a table string putStringInTable(const string& in, string tableName); // assembles one section of a tree string assembleTreeString(const string& root, vector& leaves); // converts a double to a string, with always at least one zero before and after the decimal string doubleToString(double in); int stringToInt(string in); double stringToDouble(string in); // load entire file and return its contents // inName: the path to the file to open // debug: if to print status several times to stdout // returns: the contents of the file, or an empty string if there is an error string loadEntireFile(string inName, bool debug=false); bool writeFile(const string& filename, const string& contents, bool debug=false); // returns a random character that can be an upper case letter, a lower case letter or a digit char getRandChar(); // given a hint name and a function to check if a name is unique, returns a unique string // checker should return true if the given name IS valid // if alwaysAppendRandom, it will always append a number of random digits string getUniqueString(string hint, std::function checker, bool alwaysAppendRandom=false); // run a shell command and return the output // cmd: the command to run // returns: the output of the command string runCmd(string cmd, bool printOutput=false); // get width of the current terminal // returns: width of the terminal int getTermWidth(); ================================================ FILE: h/utils/fileUtils.h ================================================ #pragma once #include "stringUtils.h" void loadFile(string filepath, string& contents); void writeFile(string filepath, const string& contents); ================================================ FILE: h/utils/stringArray.h ================================================ #pragma once #include "stringUtils.h" namespace str { void splitBy(vector& out, const string& in, const string& splitter, bool keepSplitter=false); //void splitBy(vector& out, const string& in, vector& splitters, bool keepSplitter=false); inline void splitByLine(vector& out, const string& in); int getMaxWidth(vector& in); // if size is -1 then it is determined as the max width of all the elements in out void padWidths(vector& out, int size=-1, StringPadAlignment alignment=ALIGNMENT_LEFT, string pad=" ", string leftCap="", string rightCap=""); string join(vector& in, string joiner="\n", bool addAtEnd=true); /// inline implementations inline void splitByLine(vector& out, const string& in) { splitBy(out, in, "\n"); } } ================================================ FILE: h/utils/stringDrawing.h ================================================ #pragma once #include "stringUtils.h" namespace str { // if max width is -1, it will autodetect string getBoxedString(const string& in, string boxName="", bool showLineNums=false, bool alwaysWidthMax=false, int maxWidth=-1); // it is assumed that the data is already padded void putArrayInTreeNodeBox(vector& data); string putStringInTreeNodeBox(const string& in); // note that the input may have newlines in each element string makeList(vector& data); string makeRootUpBinaryTree(const string& root, const string& leftBranch, const string& rightBranch, const string& leftLeaf, const string& rightLeaf); //string makeTreeSection(const string& root, vector& leaves); } ================================================ FILE: h/utils/stringNumConversion.h ================================================ #pragma once #include "stringUtils.h" namespace str { // returns an escape code that can be placed in a C++ string literal and will evaluate to the given char string charToCppStringLiteralEscaped(unsigned char c); // returns a base number with 0-9 being 0-9, 10-35 being a-z and 36-61 being A-Z // if maxDigits is >=0 will return only the leftmost maxDigits string intToBase62(unsigned int in, int maxDigits=-1); // uses intToBase62 to convert ptrIn to a string that can be used in a C++ id string ptrToUniqueStr(void* ptrIn, int digits=4); } ================================================ FILE: h/utils/stringUtils.h ================================================ #pragma once /// This string util header will slowly replace msclStringFuncs.h #include using std::string; #include using std::vector; namespace str { inline void nextGlyph(int& out, const string& in); inline int getWidth(const string& in); // returns the byte location of the given glyph inline int seek(const string& in, int distGlyph, int startPosByte=0); // currently is a straight equals, but in the future could do things such as evaluate strings with different types of newlines to equal inline bool subMatches(const string& in, int posBytes, const string& sub); // if endGlyph is -1 it takes till the end of the string inline string sub(const string& in, int startGlyph, int endGlyph); inline bool matches(const string& a, const string& b); inline bool hasPrefix(const string& in, const string& prefix); inline bool hasSuffix(const string& in, const string& suffix); // returns the BYTE location (not glyph location), or -1 if pattern doesn't appear //int searchFor(const string& in, string pattern, int startByte); // returns the glyph position of the first occurrence of pattern, or -1 if it doesn't appear int getGlyphPosOf(const string& in, string pattern); string tabsToSpaces(const string& in, int tabWidth=4); enum StringPadAlignment {ALIGNMENT_LEFT=1, ALIGNMENT_CENTER=0, ALIGNMENT_RIGHT=-1}; // alignment: 1 = left, -1 = right, 0 = center // pad is assumed to be of width 1 string pad(const string& in, int size, StringPadAlignment alignment=ALIGNMENT_LEFT, string pad=" ", string leftCap="", string rightCap=""); /// inline implementations inline void nextGlyph(int& out, const string& in) { do { out++; } while (out<(int)in.size() && (in[out] & 0x80) && !(in[out] & 0x40)); } inline int getWidth(const string& in) { int glyphPos=0; int bytePos=0; while (bytePos<(int)in.size()) { nextGlyph(bytePos, in); glyphPos++; } return glyphPos; } inline int seek(const string& in, int distGlyph, int startPosByte) { int i=startPosByte; while (distGlyph>0) { nextGlyph(i, in); distGlyph--; } return i; } inline bool subMatches(const string& in, int posBytes, const string& sub) { if (posBytes<0 || sub.size()+posBytes>in.size()) return false; for (int i=0; i<(int)sub.size(); i++) { if (in[i+posBytes]!=sub[i]) return false; } return true; } inline string sub(const string& in, int startGlyph, int endGlyph) { int startByte=seek(in, startGlyph); int endByte=(endGlyph < 0 ? (int)in.size() : seek(in, endGlyph-startGlyph, startByte)); return in.substr(startByte, endByte-startByte); } inline bool matches(const string& a, const string& b) { if (a.size()!=b.size()) return false; return subMatches(a, 0, b); } inline bool hasPrefix(const string& in, const string& prefix) { return subMatches(in, 0, prefix); } inline bool hasSuffix(const string& in, const string& suffix) { return subMatches(in, in.size()-suffix.size(), suffix); } } #include "stringArray.h" #include "stringDrawing.h" ================================================ FILE: other/pinecone.pn ================================================ print: argLen.String + " args:" i: 0 | i < argLen | i: i + 1 @ ( print: arg: i ) ================================================ FILE: other/pinecone_concept.txt ================================================ # Programming language concept by Sophie Winter # This is not ment to be actual code, just a place to exparament with different ideas of how the language might look add={a=$ b=$ a+b} a=7 b=3 z={sqrt(@)_} add=z add(&w &x w+x}, b) b==3? print("yes") :b==4? print("4") :{ print(z) add() } print(add(a)) max= { a,b,c=$ a>b && a>c? a : b>c? b : c } max= { a,b=$ a>b?a:b } max: a, b, c print: "hello world, the numer is " 7 a>b?a,b car.drive "hello world".print 7,5.max b:7 g:9.81 str:"hello world" zero:Int ___________________________________________________ max$: { Int: z >.z } $func: { a,b:$(Dub, Dub) } func: { a:tru b:fls a? ( print: "hello" 9 ),( b? print: "world" 7) } func: (a: Int, b: Int): { a>b? a+=3, ~b i: 0, i<6, i++ @ ( b-- b<0? ~7 ) } ___________________________________________________ in: {Int, Int} funcs: {Dub, Dub}, {Int, Bool} out: error? #1? in: {Int, Int} funcs: {Dub, Int}, {Int, Dub} out: error because tie in: {Int, Int} funcs: {Int, Bool}, {Dub, Bool} out: #1 in: {Int, Dub} funcs: {Int, Int}, {Dub, Dub} out: #2 in: {Bool, Bool} funcs: {Int, Int}, {Dub, Dub} out: error because tie (only cares if more or less complex, not how much) ___________________________________________________ max: {a: Int, b: Int}: ~a>b?a|b i: 0..8@ ( ) (i=0; i {Int} [ ] func :: {me: Int}.{a: Dub} > {Int} [ ] ___________________________________________________ \\ this is block comment // // this is block comment \\ \\ it looks great on one line too // ___________________________________________________ BaseClass :: { foo: Dub } MyObj :: { $super: BaseClass a: Int b: Int } MyObj :: {} > {MyObj}: ( return: (9.3), 8, 12 ) addToBoth :: {$me: MyObj}.{}: ( a+: 1 b+: 1 foo ) myPrint: tru, 12, 8.9 obj: MyObj obj.addToBoth ___________________________________________________ a: b: 7-1-1 : / \ a : / \ b - / \ - 1 / \ 7 1 ___________________________________________________ a: {} a.b: 8 a.c: "hello" a . getString :: {} [ c + " " + b ] MyClass: {} [ me.name: "Sophie" me.age: 19 me ] getString: {MyClass}.{} [ name + " is " + age + " years old." ] person: MyClass print: person.getString > Sophie is 19 years old ___________________________________________________ print "hello" myFunc 8? print "yes" | print "no" myFunc {Int} [ ] ___________________________________________________ a.b.c: 8 : / \ . 8 / \ . c / \ a b ___________________________________________________ i: 0 | i<100 | i: i+1 @ ( # do stuff ) (i:0; i<100; i:i+1) @ ( # do stuff ) ___________________________________________________ myFunc :: {Int}.{Int} -> {Int}: ( print: right print: left print right>6 ? left.myFunc: right-1 8 ) myFunc :: Int.Int: Int ( print: right print: left print right>6 ? left.myFunc: right-1 8 ) ___________________________________________________ myObj: MyObj: 9, 3 myPtr: &myObj myObj = myPtr ? ( # valid ) myObj2 = myPtr.me ___________________________________________________ myObj: MyObj.new: 9, 3 myPtr: myObj.ptr myObj = myPtr ? ( # valid ) myObj2 = myPtr.me ___________________________________________________ myFunc :: String.Int -> Dub [ ] a: 9.7 b: int: a ___________________________________________________ var a a(c: 9, d: 3) ___________________________________________________ a: c 9, d 3 ___________________________________________________ a: happy: MyClass b: MyClass b: a.diff ___________________________________________________ ================================================ FILE: other/readme.md ================================================ this directory is for miscellaneous files that don't really go anywhere else. ================================================ FILE: other/user_testing.txt ================================================ # what do you think the following code snippits mean? # 1 _______________________________________________________________________ a>b? ~a, ~b # 2 _______________________________________________________________________ a: (b: Dub, c: Dub). ( # more code here ) # 3 _______________________________________________________________________ a: ( (b: Dub, c: Dub)=$ # more code here ) # 4 _______________________________________________________________________ a: { b: Int c: Dub d: {Bool Bool} } # would you expect the following code to throw an error (assume no other code in the program)? # if not, what would you expect it to output? # 5 _______________________________________________________________________ i: 0, i<6, i++ @ print: i # 6 _______________________________________________________________________ a: 3 b: 2.8 print: a+b # 7 _______________________________________________________________________ a: 3 b: 2.8 print: a+(Int: b) # 8 _______________________________________________________________________ a: Int print: a a+=1 print: a # 9 # do you prefer ++a, or a++ to increment a variable? a: 5 print: a++ ================================================ FILE: readme.md ================================================ # The Pinecone Programming Language **_Built from the ground up to be fast, concise and intuitive._** ## NOTE: PINECONE IS NO LONGER BEING ACTIVELY DEVELOPED OR MAINTAINED ## Pinecone is a new programming language. Its goal is to combine the simplicity of a dynamic language with the performance of a compiled one. It is under rapid development, but most of the core features are ready. __If you want to program in Pinecone now, see the [tutorials](tutorials/index.md) for how to get started.__ __There is also a Pinecone plugin for VIM [here](https://github.com/wsKilljoy/vim-pinecone).__ __For updates, discussion and help, take a look at the Pinecone subreddit: [/r/PineconeLang](https://www.reddit.com/r/PineconeLang/)__ ## About Pinecone is a brand new, easy to learn, general purpose, multi-paradigm, high performance programming language created by Sophie Winter. Work on the language began on October 4th, 2016. Pinecone can now be interpreted or transpiled to C++. The language is written from scratch (it includes an integrated lexer, parser and interpreter, etc.). ## Example Here is the common demo program FizzBuzz written in Pinecone. It prints the numbers from 1 to 20, but it prints "Fizz" if the number is divisible by 3, "Buzz" if it is divisable by 5 and "FizzBuzz" if it is divisible by both. You can find more samples in the [examples directory](https://github.com/william01110111/Pinecone/tree/master/examples) or the [tutorials](https://github.com/william01110111/Pinecone/tree/master/tutorials). ``` # FizzBuzz # call the function defined below fizzBuzz: 1, 20 # define the FizzBuzz function fizzBuzz :: {start: Int, end: Int}: ( # loop i from start to end i: in.start | i <= in.end | i: i+1 @ ( # use conditionals to print the right thing i % 3 = 0 && i % 5 = 0 ? print: "FizzBuzz" | i % 3 = 0 ? print: "Fizz" | i % 5 = 0 ? print: "Buzz" | print: i ) ) ``` ## Why? This is probably the most common reaction to hearing about a new language. I realize that there are a __lot__ of programming languages, and that the reason for that is that there are so many assholes like me who keep making them. I do truly think, though, that Pinecone fills a previously empty niche. Pinecone aims to have similar capabilities to modern object oriented compiled languages such as C++, Swift and Rust. It's primary attraction is the simplicity and consistency of it's syntax. Here are some examples of how Pinecone is different from other popular languages: * Variable creation is implicit, just set a variable and it is created. * Variables are statically typed, but type deduction is automatic. * Calling a function that takes no arguments is the same syntax as accessing a variable (just writing it's name). * Calling a function that takes one argument is the same syntax as setting or creating a variable (`funcOrVar: input`). * Calling a function that takes multiple arguments is the same syntax as setting or creating a tuple (`funcOrTuple: input1, input2`). * White space is ignored _and_ semicolons are not necessary * `:` is used for assignment, which leaves `=` free for comparison, rather than the often confusing `==`. * Tuples, structs and classes are all basically the same thing * Functions can be sent arguments from the left side, right side or both (`inputLeft.function: inputRight`), which is used for class methods but can also allow you to define functions for any type (even primitive). * Program control is done with operators instead of keywords (`?` instead of `if`) ## Compatibility Pinecone currently requires zero external dependencies (and the only one that will likely be added is LLVM). You will need a C++11 compiler to build it, and the process is easier with GCC and Make. Pinecone has been successfully tested on Linux, MacOS and Windows. ## Current State The features that are currently implemented are as follows: * Primitive data types `Bool`, `Int` and `Dub` * All the operators you would expect (`+`, `*`, `%`, `:`, `=`, `>`, `<=`, `&&`, etc.) * Single and multi line comments * Flow control (if, if/else, while loop, for loop) * Constants * Data structs * Tuples * Int arrays * Functions * Strings and various String operations * User input * Running system commands * Interpreter for rapid development and simplicity * Transpiler to C++ for max performance The following features are coming soon: * Whatev type (equivalent to templates or generics in other languages) * Arrays of any type (Whatev support needed) * Pass-by-reference * Proper classes (pass-by-reference needed) * Operator overloading ## Contributing I have not yet added enough documentation to the language internals to make it practical for others to contribute to the language itself. If you are interested in adding a specific feature or just helping out, post in the [subreddit](https://www.reddit.com/r/PineconeLang/) or direct message me on [reddit](www.reddit.com/u/william01110111/) or [twitter](https://twitter.com/PineconeLang). Fixes and improvements to the readmes and tutorials are always welcome. ================================================ FILE: repl/readme.md ================================================ ## Repl `repl.sh` is a BASH script that creates a Pinecone repl. It supports calling functions and variables, but does not properly handle all symbols (such as `?`) and does not work for multi line expressions. To run the repl (after pinecone has been installed): * Make the `repl.sh` excitable: `chmod +x repl/repl.sh` * Run with `./repl/repl.sh` * Optional: Create an alias. Add `alias pinecone=".../path/to/pinecone/repl/repl.sh"` to your `.rc` file (`~/.bashrc`, `~/.zshrc`, ect.). Commands: * `.clear`: Clears the repl. * `.read` : Prints what is in the repl. * `.exit` : Exits the repl (same as ^C). ================================================ FILE: repl/repl.sh ================================================ #!/bin/bash echo "" > ./repl/tmp.pn; while [ true ] do read -p "> " cmd; if [ "$cmd" == ".clear" ]; then echo "" > ./repl/tmp.pn; clear; echo "[cleared]"; elif [ "$cmd" == ".exit" ]; then exit 0; elif [ "$cmd" == ".read" ]; then cat ./repl/tmp.pn; else echo $cmd >> ./repl/tmp.pn; echo " -- "; ./pinecone ./repl/tmp.pn; fi done ================================================ FILE: src/Actions/Action.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" #include "../../h/msclStringFuncs.h" ActionData::ActionData(Type returnTypeIn, Type inLeftTypeIn, Type inRightTypeIn) { returnType=returnTypeIn; inLeftType=inLeftTypeIn; inRightType=inRightTypeIn; if (!returnType || !inLeftType || !inRightType) { throw PineconeError("ActionData created with null type", INTERNAL_ERROR); } } string ActionData::toString() { return description; //return returnType->getName() + " <- " + inLeftType->getName() + " " + text + " " + inRightType->getName(); } string ActionData::getTypesString() { return returnType->getString()+" <- "+inLeftType->getString()+"."+inRightType->getString(); } //LambdaAction::LambdaAction(Type returnTypeIn, function lambdaIn, Type inLeftTypeIn, Type inRightTypeIn, string textIn) //void* LambdaAction::execute(void* inLeft, void* inRight) class VoidAction: public ActionData { public: VoidAction(): ActionData(Void, Void, Void) { setDescription("Void Action"); } void* execute(void* inLeft, void* inRight) { return nullptr; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { if (prog->getExprLevel()>0) prog->comment("void"); } string getDescription() { return str::putStringInTreeNodeBox("void"); } }; class LambdaAction: public ActionData { public: LambdaAction(Type inLeftTypeIn, Type inRightTypeIn, Type returnTypeIn, function lambdaIn, function cppWriterIn, string textIn) : ActionData(returnTypeIn, inLeftTypeIn, inRightTypeIn) { if (cppWriterIn==nullptr) { cppWriter=[=](Action inLeft, Action inRight, CppProgram* prog) { prog->comment("lambda action '"+textIn+"' has not yet been implemented for C++"); }; } else { cppWriter=cppWriterIn; } if (lambdaIn==nullptr) { lambdaIn=[=](void* inLeft, void* inRight)->void* { throw PineconeError("action '"+textIn+"' has not yet been implemented for the interpreter", RUNTIME_ERROR); }; } else { lambda=lambdaIn; } setDescription(textIn); } void* execute(void* inLeft, void* inRight) { return lambda(inLeft, inRight); } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { cppWriter(inLeft, inRight, prog); } string getDescription() { return str::putStringInTreeNodeBox(description); } private: function lambda; function cppWriter; string cppCode; }; Action lambdaAction(Type inLeftTypeIn, Type inRightTypeIn, Type returnTypeIn, function lambdaIn, function cppWriter, string textIn) { return Action(new LambdaAction(inLeftTypeIn, inRightTypeIn, returnTypeIn, lambdaIn, cppWriter, textIn)); } Action createNewVoidAction() { return Action(new VoidAction()); } ================================================ FILE: src/Actions/BoolOpAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" #include "../../h/utils/stringDrawing.h" class AndAction: public ActionData { public: AndAction(Action firstActionIn, Action secondActionIn) :ActionData(Bool, Void, Void) { firstAction=firstActionIn; secondAction=secondActionIn; if (firstAction->getReturnType()!=Bool) { throw PineconeError("AndAction created with first action that does not return Bool", INTERNAL_ERROR); } if (secondAction->getReturnType()!=Bool) { throw PineconeError("AndAction created with second action that does not return Bool", INTERNAL_ERROR); } } string getDescription() { return str::makeRootUpBinaryTree("&&", firstAction->getReturnType()->getName(), secondAction->getReturnType()->getName(), firstAction->getDescription(), secondAction->getDescription()); //return firstAction->getDescription() + " && " + firstAction->getDescription(); } void* execute(void* inLeft, void* inRight) { bool* out=(bool*)malloc(sizeof(bool)); *out=false; void* firstVal=firstAction->execute(nullptr, nullptr); if (*((bool*)firstVal)) { void* secondVal=secondAction->execute(nullptr, nullptr); if (*((bool*)secondVal)) { *out=true; } free(secondVal); } free(firstVal); return out; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { prog->pushExpr(); firstAction->addToProg(voidAction, voidAction, prog); prog->popExpr(); prog->code(" && "); prog->pushExpr(); secondAction->addToProg(voidAction, voidAction, prog); prog->popExpr(); } private: Action firstAction; Action secondAction; }; class OrAction: public ActionData { public: OrAction(Action firstActionIn, Action secondActionIn) :ActionData(Bool, Void, Void) { firstAction=firstActionIn; secondAction=secondActionIn; if (firstAction->getReturnType()!=Bool) { throw PineconeError("OrAction created with first action that does not return Bool", INTERNAL_ERROR); } if (secondAction->getReturnType()!=Bool) { throw PineconeError("OrAction created with second action that does not return Bool", INTERNAL_ERROR); } } string getDescription() { return str::makeRootUpBinaryTree("||", firstAction->getReturnType()->getName(), secondAction->getReturnType()->getName(), firstAction->getDescription(), secondAction->getDescription()); } void* execute(void* inLeft, void* inRight) { bool* out=(bool*)malloc(sizeof(bool)); *out=true; void* firstVal=firstAction->execute(nullptr, nullptr); if (!*((bool*)firstVal)) { void* secondVal=secondAction->execute(nullptr, nullptr); if (!*((bool*)secondVal)) { *out=false; } free(secondVal); } free(firstVal); return out; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { prog->pushExpr(); firstAction->addToProg(voidAction, voidAction, prog); prog->popExpr(); prog->code(" || "); prog->pushExpr(); secondAction->addToProg(voidAction, voidAction, prog); prog->popExpr(); } private: Action firstAction; Action secondAction; }; Action andAction(Action firstActionIn, Action secondActionIn) { return Action(new AndAction(firstActionIn, secondActionIn)); } Action orAction(Action firstActionIn, Action secondActionIn) { return Action(new OrAction(firstActionIn, secondActionIn)); } ================================================ FILE: src/Actions/BranchAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" #include "../../h/CppProgram.h" #include "../../h/utils/stringDrawing.h" class BranchAction: public ActionData { public: BranchAction(Action leftInputIn, Action actionIn, Action rightInputIn) :ActionData(actionIn->getReturnType(), Void, Void) { if (!actionIn) throw PineconeError(string() + "branch action created sent null action", INTERNAL_ERROR); if (!leftInputIn) throw PineconeError(string() + "branch action created sent null leftInput", INTERNAL_ERROR); if (!rightInputIn) throw PineconeError(string() + "branch action created sent null rightInput", INTERNAL_ERROR); action=actionIn; leftInput=leftInputIn; rightInput=rightInputIn; if (!leftInput->getInLeftType()->matches(Void) || !leftInput->getInRightType()->matches(Void)) { throw PineconeError(leftInput->getDescription() + " put into branch even though its inputs are not void", INTERNAL_ERROR); } if (!rightInput->getInLeftType()->matches(Void) || !rightInput->getInRightType()->matches(Void)) { throw PineconeError(rightInput->getDescription() + " put into branch even though its inputs are not void", INTERNAL_ERROR); } if (!leftInput->getReturnType()->matches(action->getInLeftType())) { throw PineconeError(leftInput->getDescription() + " return type is not the same as the left input of " + action->getDescription(), INTERNAL_ERROR); } if (!rightInput->getReturnType()->matches(action->getInRightType())) { throw PineconeError(rightInput->getDescription() + " return type is not the same as the right input of " + action->getDescription(), INTERNAL_ERROR); } } string getDescription() { if (leftInput && action && rightInput) { return str::makeRootUpBinaryTree(action->getDescription(), leftInput->getReturnType()->getName(), rightInput->getReturnType()->getName(), leftInput->getDescription(), rightInput->getDescription()); //return getReturnType()->toString() + " <- [" + leftInput->getDescription() + "].[" + action->getDescription() + "]:[" + rightInput->getDescription() + "]"; //return "(" + leftInput->getDescription() + " -> " + action->getDescription() + " <- " + rightInput->getDescription() + ")"; //return getReturnType()->getName() + " <- " + leftInput->getDescription() + "." + action->getDescription() + ":" + rightInput->getDescription(); } else return "[branch with null element]"; } void* execute(void* inLeft, void* inRight) { void* leftData=leftInput->execute(nullptr, nullptr); void* rightData=rightInput->execute(nullptr, nullptr); void* outData=action->execute(leftData, rightData); free(leftData); free(rightData); return outData; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { Action leftInTmp = leftInput; Action rightInTmp = rightInput; if (leftInTmp->getReturnType()!=action->getInLeftType()) leftInTmp=cppTupleCastAction(leftInTmp, action->getInLeftType()); if (rightInTmp->getReturnType()!=action->getInRightType()) rightInTmp=cppTupleCastAction(rightInTmp, action->getInRightType()); action->addToProg(leftInTmp, rightInTmp, prog); } private: Action action; Action leftInput; Action rightInput; }; class RightBranchAction: public ActionData { public: RightBranchAction(Action actionIn, Action rightInputIn) :ActionData(actionIn->getReturnType(), Void, Void) { if (!actionIn) throw PineconeError(string() + "branch action created sent null action", INTERNAL_ERROR); if (!rightInputIn) throw PineconeError(string() + "branch action created sent null rightInput", INTERNAL_ERROR); action=actionIn; rightInput=rightInputIn; if (!rightInput->getInLeftType()->matches(Void) || !rightInput->getInRightType()->matches(Void)) { throw PineconeError(rightInput->getDescription() + " put into branch even though its inputs are not void", INTERNAL_ERROR); } if (!rightInput->getReturnType()->matches(action->getInRightType())) { throw PineconeError(rightInput->getDescription() + " return type is not the same as the right input of " + action->getDescription(), INTERNAL_ERROR); } } ~RightBranchAction() { } string getDescription() { if (action && rightInput) { return str::makeRootUpBinaryTree(action->getDescription(), "", rightInput->getReturnType()->getName(), "", rightInput->getDescription()); //return getReturnType()->toString() + " <- [" + leftInput->getDescription() + "].[" + action->getDescription() + "]:[" + rightInput->getDescription() + "]"; //return "(" + action->getDescription() + " <- " + rightInput->getDescription() + ")"; //return getReturnType()->getName() + " <- " + leftInput->getDescription() + "." + action->getDescription() + ":" + rightInput->getDescription(); } else return "[branch with null element]"; } void* execute(void* inLeft, void* inRight) { void* rightData=rightInput->execute(nullptr, nullptr); void* outData=action->execute(nullptr, rightData); free(rightData); return outData; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { Action rightInTmp = rightInput; if (rightInTmp->getReturnType()!=action->getInRightType()) rightInTmp=cppTupleCastAction(rightInTmp, action->getInRightType()); action->addToProg(voidAction, rightInTmp, prog); } private: Action action=nullptr; Action rightInput=nullptr; }; class LeftBranchAction: public ActionData { public: LeftBranchAction(Action leftInputIn, Action actionIn) :ActionData(actionIn->getReturnType(), Void, Void) { if (!actionIn) throw PineconeError(string() + "branch action created sent null action", INTERNAL_ERROR); if (!leftInputIn) throw PineconeError(string() + "branch action created sent null leftInput", INTERNAL_ERROR); action=actionIn; leftInput=leftInputIn; if (!leftInput->getInLeftType()->matches(Void) || !leftInput->getInRightType()->matches(Void)) { throw PineconeError(leftInput->getDescription() + " put into branch even though its inputs are not void", INTERNAL_ERROR); } if (!leftInput->getReturnType()->matches(action->getInLeftType())) { throw PineconeError(leftInput->getDescription() + " return type is not the same as the left input of " + action->getDescription(), INTERNAL_ERROR); } } string getDescription() { if (leftInput && action) { return str::makeRootUpBinaryTree(action->getDescription(), leftInput->getReturnType()->getName(), "", leftInput->getDescription(), ""); //return getReturnType()->toString() + " <- [" + leftInput->getDescription() + "].[" + action->getDescription() + "]:[" + rightInput->getDescription() + "]"; //return "(" + leftInput->getDescription() + " -> " + action->getDescription() + ")"; //return getReturnType()->getName() + " <- " + leftInput->getDescription() + "." + action->getDescription() + ":" + rightInput->getDescription(); } else return "[branch with null element]"; } void* execute(void* inLeft, void* inRight) { void* leftData=leftInput->execute(nullptr, nullptr); void* outData=action->execute(leftData, nullptr); free(leftData); return outData; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { Action leftInTmp = leftInput; if (leftInTmp->getReturnType()!=action->getInLeftType()) leftInTmp=cppTupleCastAction(leftInTmp, action->getInLeftType()); action->addToProg(leftInTmp, voidAction, prog); } private: Action leftInput; Action action; }; Action branchAction(Action leftInputIn, Action actionIn, Action rightInputIn) { //return Action(new BranchAction(leftInputIn, actionIn, rightInputIn)); if (leftInputIn->getReturnType()->isVoid()) { if (rightInputIn->getReturnType()->isVoid()) { return actionIn; } else { return Action(new RightBranchAction(actionIn, rightInputIn)); } } else { if (rightInputIn->getReturnType()->isVoid()) { return Action(new LeftBranchAction(leftInputIn, actionIn)); } else { return Action(new BranchAction(leftInputIn, actionIn, rightInputIn)); } } } ================================================ FILE: src/Actions/FunctionAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" #include "../../h/StackFrame.h" #include "../../h/AstNode.h" #include "../../h/utils/stringNumConversion.h" #include "../../h/utils/stringUtils.h" #include //for memcpy using std::memcpy; class FunctionAction: public ActionData { public: FunctionAction(Action actionIn, shared_ptr stackFameIn): ActionData(actionIn->getReturnType(), stackFameIn->getLeftInType(), stackFameIn->getRightInType()) { stackFame=stackFameIn; action=actionIn; node=nullptr; setDescription("function ("+getInLeftType()->getString()+"."+getInRightType()->getString()+" > "+getReturnType()->getString()+")"); if (action->getInLeftType()!=Void || action->getInRightType()!=Void) { throw PineconeError(action->getDescription() + " put into function even though its inputs are not void", INTERNAL_ERROR); } } FunctionAction(AstNode nodeIn, Type returnTypeIn, shared_ptr stackFameIn): ActionData(returnTypeIn, stackFameIn->getLeftInType(), stackFameIn->getRightInType()) { stackFame=stackFameIn; node=move(nodeIn); action=nullptr; setDescription("function ("+getInLeftType()->getString()+"."+getInRightType()->getString()+" > "+getReturnType()->getString()+")"); } void resolveAction() { if (!node || action) { throw PineconeError("FunctionAction::resolveAction called when this action is in the wrong state", INTERNAL_ERROR); } action=node->getAction(); if (!returnType->isVoid() && !returnType->matches(action->getReturnType())) { // the objects that are being returned are being destroyed before the function exits // also another problem: if a function returns a thing but nothing accepts the output, the destructor is not called //throw PineconeError("function body returns "+action->getReturnType()->getString()+" instead of "+returnType->getString(), SOURCE_ERROR, node->getToken()); throw PineconeError("function body returns "+action->getReturnType()->getString()+" instead of "+returnType->getString()+"\n"+action->getDescription(), SOURCE_ERROR, node->getToken()); } if (!action->getInLeftType()->isVoid() || !action->getInRightType()->isVoid()) { throw PineconeError(action->getDescription() + " put into function even though its inputs are not void", INTERNAL_ERROR); } } string getDescription() { return str::putStringInTreeNodeBox("call func "+nameHint); //if (!action) // resolveAction(); //return "func: " + description;//action->getDescription(); } bool isFunction() {return true;} void* execute(void* inLeft, void* inRight) { if (!action) resolveAction(); void * oldStackPtr=stackPtr; stackPtr=malloc(stackFame->getSize()); if (inLeft) memcpy((char*)stackPtr+stackFame->getLeftOffset(), inLeft, getInLeftType()->getSize()); if (inRight) memcpy((char*)stackPtr+stackFame->getRightOffset(), inRight, getInRightType()->getSize()); void* out=action->execute(nullptr, nullptr); free(stackPtr); stackPtr=oldStackPtr; return out; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { if (!action) { resolveAction(); } // construct a unique name that will NEVER collide with another funtion (even in the case of overloaded functions with the same Pinecone name) // note that this name will be used to determine if this function has already been used in C++, so it should generate the same name every time string name="%"; name+=(nameHint.empty() ? "func" : nameHint); name+="_"+str::ptrToUniqueStr(&*action); //name+="_"+getInLeftType()->getCompactString()+"_"+getInRightType()->getCompactString(); /* if (str::hasSuffix(nameHint, "_CPP")) name=nameHint.substr(0, nameHint.size()-4); */ if (!prog->hasFunc(name)) { prog->pushFunc(name, getInLeftType(), getInRightType(), getReturnType()); if (getReturnType()->isCreatable() && action->getReturnType()!=getReturnType()) { cppTupleCastAction(action, getReturnType())->addToProg(prog); } else { action->addToProg(prog); } prog->endln(); prog->popFunc(); } prog->name(name); prog->pushExpr(); /* bool hasStarted=false; if (getInLeftType()->getType()==TypeBase::TUPLE) { for (auto i: *getInLeftType()->getAllSubTypes()) { if (hasStarted) prog->code(", "); hasStarted=true; getElemFromTupleAction(getInLeftType(), i.name)->addToProg(inLeft, voidAction, prog); } } else if (!getInLeftType()->isCreatable()) { // do nothing } else { if (hasStarted) prog->code(", "); hasStarted=true; inLeft->addToProg(prog); } if (getInRightType()->getType()==TypeBase::TUPLE) { for (auto i: *getInRightType()->getAllSubTypes()) { if (hasStarted) prog->code(", "); hasStarted=true; getElemFromTupleAction(getInRightType(), i.name)->addToProg(inRight, voidAction, prog); } } else if (!getInRightType()->isCreatable()) { // do nothing } else { if (hasStarted) prog->code(", "); hasStarted=true; inRight->addToProg(prog); } //prog->code(", "); //inRight->addToProg(prog); */ if (getInLeftType()->isCreatable()) { inLeft->addToProg(prog); } if (getInRightType()->isCreatable()) { if (getInLeftType()->isCreatable()) prog->code(", "); inRight->addToProg(prog); } prog->popExpr(); //prog->comment("function transpiling not yet implemented"); } private: shared_ptr stackFame; Action action=nullptr; AstNode node=nullptr; }; Action functionAction(Action actionIn, shared_ptr stackFameIn) { return Action(new FunctionAction(actionIn, stackFameIn)); } Action functionAction(AstNode nodeIn, Type returnTypeIn, shared_ptr stackFameIn) { return Action(new FunctionAction(move(nodeIn), returnTypeIn, stackFameIn)); } ================================================ FILE: src/Actions/IfAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" class IfAction: public ActionData { public: IfAction(Action conditionIn, Action ifActionIn) :ActionData(Void, Void, Void) { condition=conditionIn; ifAction=ifActionIn; if (condition->getReturnType()!=Bool) { error.log("IfAction created with condition action that does not return Bool", INTERNAL_ERROR); } if (condition->getInLeftType()!=Void || condition->getInRightType()!=Void) { error.log("IfAction created with condition action that takes in something other then Void", INTERNAL_ERROR); } if (ifAction->getInLeftType()!=Void || ifAction->getInRightType()!=Void) { error.log("IfAction created with action that takes in something other then Void", INTERNAL_ERROR); } } string getDescription() { return str::makeRootUpBinaryTree("?", condition->getReturnType()->getName(), "", condition->getDescription(), ifAction->getDescription()); //return "if " + condition->getDescription() + " then " + ifAction->getDescription(); } void* execute(void* inLeft, void* inRight) { void* conditionOut=condition->execute(nullptr, nullptr); if (*((bool*)conditionOut)) { free(ifAction->execute(nullptr, nullptr)); } free(conditionOut); return nullptr; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { prog->code("if "); prog->pushExpr(); condition->addToProg(voidAction, voidAction, prog); prog->popExpr(); prog->pushBlock(); ifAction->addToProg(voidAction, voidAction, prog); prog->endln(); prog->popBlock(); } private: Action condition; Action ifAction; }; class IfElseAction: public ActionData { public: IfElseAction(Action conditionIn, Action ifActionIn, Action elseActionIn) :ActionData([&](){return ifActionIn->getReturnType()->matches(elseActionIn->getReturnType())?ifActionIn->getReturnType():Void;}(), Void, Void) { returnVal=getReturnType()!=Void; condition=conditionIn; ifAction=ifActionIn; elseAction=elseActionIn; if (condition->getReturnType()!=Bool) { error.log("IfElseAction created with condition action that does not return Bool", INTERNAL_ERROR); } if (condition->getInLeftType()!=Void || condition->getInRightType()!=Void) { error.log("IfElseAction created with conditiofn action that takes in something other then Void", INTERNAL_ERROR); } if (ifAction->getInLeftType()!=Void || ifAction->getInRightType()!=Void) { error.log("IfElseAction created with action that takes in something other then Void", INTERNAL_ERROR); } } string getDescription() { string branch=str::makeRootUpBinaryTree("╭┴╮\n│ │", "fls", "tru", elseAction->getDescription(), ifAction->getDescription()); return str::makeRootUpBinaryTree("?", condition->getReturnType()->getName(), "", condition->getDescription(), branch); //return "if " + condition->getDescription() + " then " + ifAction->getDescription(); } /* string getCSource(string inLeft, string inRight) { return "if (" + condition->getCSource() + ")\n{\n" + ifAction->getCSource() + "\n} else {\n" + elseAction->getCSource() + "\n}"; } */ void* execute(void* inLeft, void* inRight) { void* out; void* conditionOut=condition->execute(nullptr, nullptr); if (*((bool*)conditionOut)) { out=ifAction->execute(nullptr, nullptr); } else { out=elseAction->execute(nullptr, nullptr); } free(conditionOut); if (returnVal) { return out; } else { free(out); return nullptr; } } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { if (returnVal && prog->getExprLevel()>0) { prog->pushExpr(); condition->addToProg(voidAction, voidAction, prog); prog->popExpr(); prog->code(" ? "); prog->pushExpr(); ifAction->addToProg(voidAction, voidAction, prog); prog->popExpr(); prog->code(" : "); prog->pushExpr(); elseAction->addToProg(voidAction, voidAction, prog); prog->popExpr(); } else { prog->code("if "); prog->pushExpr(); condition->addToProg(voidAction, voidAction, prog); prog->popExpr(); prog->pushBlock(); ifAction->addToProg(voidAction, voidAction, prog); prog->endln(); prog->popBlock(); prog->code("else"); prog->pushBlock(); elseAction->addToProg(voidAction, voidAction, prog); prog->endln(); prog->popBlock(); } } private: Action condition; Action ifAction; Action elseAction; bool returnVal=false; }; Action ifAction(Action conditionIn, Action ifActionIn) { return Action(new IfAction(conditionIn, ifActionIn)); } Action ifElseAction(Action conditionIn, Action ifActionIn, Action elseAction) { return Action(new IfElseAction(conditionIn, ifActionIn, elseAction)); } ================================================ FILE: src/Actions/ListAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" class ListAction: public ActionData { public: ListAction(const vector& actionsIn, const vector& destroyersIn): ActionData ( (actionsIn.size()>0?actionsIn.back()->getReturnType():Void), Void, Void ) { actions=actionsIn; destroyers=destroyersIn; for (auto i=actions.begin(); i!=actions.end(); ++i) { if (!(*i)->getInLeftType()->matches(Void) || !(*i)->getInRightType()->matches(Void)) { error.log((*i)->getDescription() + " put into action list even though its inputs are not void", INTERNAL_ERROR); } } } ~ListAction() { } string getDescription() { vector data; for (auto i=actions.begin(); i!=actions.end(); ++i) { if (*i) data.push_back((*i)->getDescription()); else data.push_back(str::putStringInTreeNodeBox("[null action]")); } return str::makeList(data); /* string out; out+="\n{"; for (auto i=actions.begin(); i!=actions.end(); ++i) { out+="\n\t"; string str; if (*i) str=(*i)->getDescription(); else str="[null action]"; for (unsigned j=0; jexecute(nullptr, nullptr)); } void* returnVal=(*i)->execute(nullptr, nullptr); for (auto j: destroyers) { free(j->execute(nullptr, nullptr)); } return returnVal; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { addToProg(prog, getReturnType()); } void addToProg(CppProgram* prog, Type returnType) { bool shouldReturn=(prog->getBlockLevel()==0 && prog->getIfReturnsVal()) && !prog->isMain(); prog->pushBlock(); for (auto i: actions) { if (shouldReturn && i==actions.back()) { prog->declareVar("-out", returnType); prog->name("-out"); prog->code(" = "); if (i->getReturnType()!=returnType) { cppTupleCastAction(i, returnType)->addToProg(prog); } else { i->addToProg(prog); } } else { i->addToProg(prog); } prog->endln(); } for (auto i: destroyers) { i->addToProg(prog); prog->endln(); } if (shouldReturn) { prog->code("return "); prog->name("-out"); prog->endln(); } prog->popBlock(); } /* string getCSource(string inLeft, string inRight) { string out; for (auto i: actions) { out+=i->getCSource()+";\n"; } return out; } */ private: vector actions; vector destroyers; }; void addListToProgWithCppCasting(ListAction* list, Type returnType, CppProgram* prog) { list->addToProg(prog, returnType); } Action listAction(const vector& actionsIn, const vector& destroyersIn) { if (actionsIn.size()==0 && destroyersIn.size()==0) { return voidAction; } else if (actionsIn.size()==1 && destroyersIn.size()==0) { return actionsIn[0]; } else { return Action(new ListAction(actionsIn, destroyersIn)); } } ================================================ FILE: src/Actions/LoopAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" class LoopAction: public ActionData { public: LoopAction(Action conditionIn, Action endActionIn, Action loopActionIn) :ActionData(Void, Void, Void) { condition=conditionIn; loopAction=loopActionIn; endAction=endActionIn; if (condition->getReturnType()!=Bool) { error.log("LoopAction created with condition action that does not return Bool", INTERNAL_ERROR); } if (condition->getInLeftType()!=Void || condition->getInRightType()!=Void) { error.log("LoopAction created with condition action that takes in something other then Void", INTERNAL_ERROR); } if (loopAction->getInLeftType()!=Void || loopAction->getInRightType()!=Void) { error.log("LoopAction created with action that takes in something other then Void", INTERNAL_ERROR); } } string getDescription() { string body=loopAction->getDescription(); if (endAction!=voidAction) { vector data={body, endAction->getDescription()}; body=str::makeList(data); } return str::makeRootUpBinaryTree("@", condition->getReturnType()->getName(), "", condition->getDescription(), body); //return "while " + condition->getDescription() + " do " + loopAction->getDescription(); } void* execute(void* inLeft, void* inRight) { void* conditionOut; while (true) { conditionOut=condition->execute(nullptr, nullptr); if (!(*((bool*)conditionOut))) break; free(conditionOut); free(loopAction->execute(nullptr, nullptr)); free(endAction->execute(nullptr, nullptr)); } free(conditionOut); return nullptr; } /* string getCSource(string inLeft, string inRight) { string out; out+="while ("; out+=condition->getCSource(); out+=")\n{"; out+=loopAction->getCSource(); out+=endAction->getCSource(); out+="\n}"; return out; } */ void addToProg(Action inLeft, Action inRight, CppProgram* prog) { prog->code("while "); prog->pushExpr(); condition->addToProg(prog); prog->popExpr(); prog->pushBlock(); loopAction->addToProg(prog); prog->endln(); if (endAction!=voidAction) { endAction->addToProg(prog); prog->endln(); } prog->popBlock(); } private: Action condition; Action loopAction; Action endAction; }; Action loopAction(Action conditionIn, Action loopActionIn) { return Action(new LoopAction(conditionIn, voidAction, loopActionIn)); } Action loopAction(Action conditionIn, Action endActionIn, Action loopActionIn) { return Action(new LoopAction(conditionIn, endActionIn, loopActionIn)); } ================================================ FILE: src/Actions/MakeTupleAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" #include #include using std::vector; class GetElemFromTupleAction; class CppTupleCastAction; class ListAction; void addListToProgWithCppCasting(ListAction* list, Type returnType, CppProgram* prog); class MakeTupleAction: public ActionData { public: MakeTupleAction(const vector& sourceActionsIn): ActionData( [&]() -> Type { TupleTypeMaker tuple; for (auto i=sourceActionsIn.begin(); i!=sourceActionsIn.end(); ++i) { tuple.add((*i)->getReturnType()); } return tuple.get(true); }(), Void, Void ) { if (sourceActionsIn.size()<=0) { error.log("MakeTupleAction created with empty list", INTERNAL_ERROR); } sourceActions=sourceActionsIn; for (auto i=sourceActions.begin(); i!=sourceActions.end(); ++i) { if (!(*i)->getInLeftType()->matches(Void) || !(*i)->getInRightType()->matches(Void)) { error.log((*i)->getDescription() + " put into tuple creation even though its inputs are not void", INTERNAL_ERROR); } if ((*i)->getReturnType()->matches(Void)) { error.log((*i)->getDescription() + " put into tuple creation even though its output is void", INTERNAL_ERROR); } } } string getDescription() { return str::putStringInTreeNodeBox("make tuple of type "+getReturnType()->getName()); //return "[tuple of type " + getReturnType()->getString() + "]"; /*string out; out+="\n{"; for (auto i=sourceActions.begin(); i!=sourceActions.end(); ++i) { out+="\n\t"; string str; if (*i) str=(*i)->getDescription(); else str="[null action]"; for (unsigned j=0; jgetSize()); size_t offset=0; for (auto i=sourceActions.begin(); i!=sourceActions.end(); ++i) { void* val=(*i)->execute(nullptr, nullptr); memcpy((char*)out+offset, val, (*i)->getReturnType()->getSize()); free(val); offset+=(*i)->getReturnType()->getSize(); } return out; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { if (sourceActions.size()==1) { sourceActions[0]->addToProg(prog); return; } prog->code(prog->getTypeCode(getReturnType())); prog->pushExpr(); bool start=true; for (auto i: sourceActions) { if (!start) prog->code(", "); start=false; i->addToProg(prog); } prog->popExpr(); } private: vector sourceActions; friend GetElemFromTupleAction; friend CppTupleCastAction; }; class CppTupleCastAction: public ActionData { public: CppTupleCastAction(Action actionIn, Type returnType): ActionData(returnType, Void, Void) { if ((actionIn->getReturnType()->getType()!=TypeBase::TUPLE && getReturnType()->getType()!=TypeBase::TUPLE) || !actionIn->getReturnType()->matches(getReturnType())) { throw PineconeError("CppCastAction was only designed to cast matching tuples, which is not how it is being used", INTERNAL_ERROR); } action=actionIn; } string getDescription() { return "C++ cast"; } void* execute(void* inLeft, void* inRight) { throw PineconeError("CppCastAction was executed in the interpreter, which shouldn't happen", INTERNAL_ERROR); } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { if (getReturnType()->getAllSubTypes()->size()==1) { action->addToProg(prog); } else if (typeid(*action)==typeid(MakeTupleAction)) { MakeTupleAction * realAction=(MakeTupleAction*)&*action; prog->code(prog->getTypeCode(getReturnType())); prog->pushExpr(); for (auto i: realAction->sourceActions) { i->addToProg(prog); if (i!=realAction->sourceActions.back()) prog->code(", "); } prog->popExpr(); } else if (typeid(*action)==typeid(*listAction({voidAction, voidAction}, {}))) { addListToProgWithCppCasting((ListAction*)&*action, getReturnType(), prog); } else if (getReturnType()->getType()!=TypeBase::TUPLE) { action->addToProg(prog); prog->code("."); prog->code(action->getReturnType()->getAllSubTypes()[0][0].name); } else { string funcName=action->getReturnType()->getCompactString()+"=>"+getReturnType()->getCompactString(); if (!prog->hasFunc(funcName)) { Type argType=makeTuple({{"in", action->getReturnType()}}, true); prog->pushFunc(funcName, "", Void, argType, getReturnType()); prog->declareVar("-out", getReturnType()); auto outTypes=*getReturnType()->getAllSubTypes(); if (action->getReturnType()->getType()==TypeBase::TUPLE) { auto inTypes=*action->getReturnType()->getAllSubTypes(); for (int i=0; iname("-out"); prog->code("."); prog->code(outTypes[i].name); prog->code(" = "); prog->name("in"); prog->code("."); prog->code(inTypes[i].name); prog->endln(); } } } else { prog->name("-out"); prog->code("."); prog->code(outTypes[0].name); prog->code(" = "); prog->name("in"); prog->endln(); } prog->code("return "); prog->name("-out"); prog->endln(); prog->popFunc(); } prog->name(funcName); prog->pushExpr(); action->addToProg(prog); prog->popExpr(); } } private: Action action; friend GetElemFromTupleAction; }; class GetElemFromTupleAction: public ActionData { public: GetElemFromTupleAction(Type typeInIn, string nameIn): ActionData(typeInIn->getSubType(nameIn).type, typeInIn, Void) { typeIn=typeInIn; typeOut=typeInIn->getSubType(nameIn).type; // if no type was found, the ActionData constructor would have already thrown an error name=nameIn; size=typeOut->getSize(); offset=typeIn->getSubType(name).offset; } string getDescription() { return str::putStringInTreeNodeBox(name); //return "get element from tuple"; } void* execute(void* inLeft, void* inRight) { void* out=malloc(size); memcpy(out, (char*)inLeft+offset, size); return out; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { if (typeIn->getAllSubTypes()->size()==1) { inLeft->addToProg(prog); return; } MakeTupleAction * makeTupleAction=nullptr; if (typeid(*inLeft)==typeid(MakeTupleAction)) { makeTupleAction=(MakeTupleAction *)&*inLeft; } else if (typeid(*inLeft)==typeid(CppTupleCastAction)) { Action castAction=((CppTupleCastAction *)&*inLeft)->action; if (typeid(*castAction)==typeid(MakeTupleAction)) { makeTupleAction=(MakeTupleAction *)&*castAction; } } if (makeTupleAction) { auto types=*inLeft->getReturnType()->getAllSubTypes(); for (int i=0; isourceActions[i]->addToProg(prog); break; } } } else { /* if (inLeft->nameHint=="in" || inLeft->nameHint=="me") { prog->code(name); } else */ { inLeft->addToProg(prog); prog->code("."+name); } } } private: Type typeIn; Type typeOut; int offset; size_t size; string name; }; Action makeTupleAction(const std::vector& sourceActionsIn) { return Action(new MakeTupleAction(sourceActionsIn)); } Action getElemFromTupleAction(Type source, string name) { if (!source->getSubType(name).type) throw PineconeError("could not find '"+name+"' in "+source->getString(), SOURCE_ERROR); Action out=Action(new GetElemFromTupleAction(source, name)); return out; } Action cppTupleCastAction(Action actionIn, Type returnType) { return Action(new CppTupleCastAction(actionIn, returnType)); } ================================================ FILE: src/Actions/TypeAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" class TypeGetAction: public ActionData { public: TypeGetAction(Type typeIn): ActionData(typeIn->getMeta(), Void, Void) { setDescription(typeIn->getString()+" (type)"); } string getCSource(string inLeft, string inRight) { return "/* C source for TypeGetAction not yet implemented */"; } void* execute(void* inLeft, void* inRight) { error.log("TypeGetAction::execute called, which shouldn't happen", RUNTIME_ERROR); return nullptr; } string getDescription() { return str::putStringInTreeNodeBox("{"+getReturnType()->getName()+"}"); } private: }; Action typeGetAction(Type typeIn) { return Action(new TypeGetAction(typeIn)); } ================================================ FILE: src/Actions/VarAction.cpp ================================================ #include "../../h/Action.h" #include "../../h/ErrorHandler.h" #include "../../h/StackFrame.h" #include "../../h/CppProgram.h" #include "../../h/utils/stringNumConversion.h" #include "../../h/Namespace.h" #include "../../h/utils/stringDrawing.h" class VarGetAction: public ActionData { public: VarGetAction(size_t in, void ** stackPtrPtrIn, Type typeIn, string idIn): ActionData(typeIn, Void, Void) { offset=in; stackPtrPtr=stackPtrPtrIn; nameHint=idIn; setDescription("get " + typeIn->getString() + " '" + idIn + "'"); } void* execute(void* inLeft, void* inRight) { if (!(*stackPtrPtr)) { throw PineconeError("something fucked up big time. VarGetAction::execute called while stack pointer is still null", RUNTIME_ERROR); } void* out=malloc(returnType->getSize()); memcpy(out, (char*)(*stackPtrPtr)+offset, returnType->getSize()); return out; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { /* if ((nameHint=="me" || nameHint=="in") && getReturnType()->getType()==TypeBase::TUPLE) { prog->code(prog->getTypeCode(getReturnType())); prog->pushExpr(); bool isFirst=true; for (auto i: *getReturnType()->getAllSubTypes()) { if (!isFirst) prog->code(", "); isFirst=false; prog->name(i.name); } prog->popExpr(); } else */ { prog->declareVar(nameHint, getReturnType()); prog->name(nameHint); } } string getDescription() { return str::putStringInTreeNodeBox("get "+nameHint); } private: void ** stackPtrPtr; size_t offset; }; class VarSetAction: public ActionData { public: VarSetAction(size_t in, void ** stackPtrPtrIn, Type typeIn, string idIn): ActionData(Void, Void, typeIn) { offset=in; stackPtrPtr=stackPtrPtrIn; nameHint=idIn; setDescription("set " + typeIn->getString() + " '" + idIn + "'"); } void* execute(void* left, void* right) { if (!(*stackPtrPtr)) { throw PineconeError("something fucked up big time. VarSetAction::execute called while stack pointer is still null", RUNTIME_ERROR); } //copy data on to the stack location of the var memcpy((char*)(*stackPtrPtr)+offset, right, inRightType->getSize()); //return a new copy of the data void* out=malloc(returnType->getSize()); memcpy(out, (char*)(*stackPtrPtr)+offset, inRightType->getSize()); //return out; return nullptr; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { /* if ((nameHint=="me" || nameHint=="in") && getReturnType()->getType()==TypeBase::TUPLE) { if (prog->getExprLevel()>0) { throw PineconeError("can not set 'in' or 'me' inside expression in C++ because of some really dumb reason", INTERNAL_ERROR); } prog->pushBlock(); prog->code(prog->getTypeCode(inRight->getReturnType())); prog->code(" tmp = "); prog->pushExpr(); inRight->addToProg(prog); prog->popExpr(); prog->endln(); auto subs=*getReturnType()->getAllSubTypes(); for (int i=0; i<(int)subs.size(); i++) { prog->name(subs[i].name); prog->code(" = "); prog->code("tmp."+(*inRight->getReturnType()->getAllSubTypes())[i].name); //prog->pushExpr(); //getElemFromTupleAction(inRight->getReturnType(), (*inRight->getReturnType()->getAllSubTypes())[i].name)->addToProg(var, voidAction, prog); //prog->popExpr(); prog->endln(); } prog->popBlock(); } else */ { prog->declareVar(nameHint, getInRightType()); prog->name(nameHint); prog->code(" = "); prog->pushExpr(); inRight->addToProg(prog); prog->popExpr(); //if (prog->getExprLevel()==0) // prog->endln(); } } string getDescription() { return str::putStringInTreeNodeBox("set "+nameHint); } private: void ** stackPtrPtr; size_t offset; }; class ConstGetAction: public ActionData { public: ConstGetAction(const void* in, Type typeIn, string textIn): ActionData(typeIn, Void, Void) { data=malloc(returnType->getSize()); memcpy(data, in, returnType->getSize()); setDescription(textIn);// + " (" + typeIn.toString() + " literal)"); } ~ConstGetAction() { free(data); } void* execute(void* inLeft, void* inRight) { void* out=malloc(returnType->getSize()); memcpy(out, data, returnType->getSize()); return out; } void addToProg(Action inLeft, Action inRight, CppProgram* prog) { if (getReturnType()==String) { //addToProgPnStr(prog); //prog->name("$pnStr"); prog->code(prog->getTypeCode(String)); prog->pushExpr(); auto sizeInfo=getReturnType()->getSubType("_size"); auto dataInfo=getReturnType()->getSubType("_data"); if (sizeInfo.type!=Int || dataInfo.type!=Byte->getPtr()) { throw PineconeError("ConstGetAction::addToProg failed to access string properties", INTERNAL_ERROR); } prog->code(Int->getCppLiteral((char*)data+sizeInfo.offset, prog)); prog->code(", "); //prog->code(Int->getCppLiteral((char*)data+sizeInfo.offset, prog)); //prog->code(", (unsigned char*)\""); prog->code("(unsigned char*)\""); int len=*(int*)((char*)data+sizeInfo.offset); for (int i=0; icode("\\\""); } else if (c=='\\') { prog->code("\\\\"); } else if (c>=32 && c<=126) { prog->code(string()+c); } else if (c=='\n') { prog->code("\\n"); } else { prog->code(str::charToCppStringLiteralEscaped(c)); } } prog->code("\""); prog->popExpr(); } else { prog->code(getReturnType()->getCppLiteral(data, prog)); } } string getDescription() { return str::putStringInTreeNodeBox(description); } private: void* data; }; Action varGetAction(size_t in, Type typeIn, string textIn) { return Action(new VarGetAction(in, &stackPtr, typeIn, textIn)); } Action varSetAction(size_t in, Type typeIn, string varNameIn) { return Action(new VarSetAction(in, &stackPtr, typeIn, varNameIn)); } Action globalGetAction(size_t in, Type typeIn, string textIn) { return Action(new VarGetAction(in, &globalFramePtr, typeIn, textIn)); } Action globalSetAction(size_t in, Type typeIn, string textIn) { return Action(new VarSetAction(in, &globalFramePtr, typeIn, textIn)); } Action constGetAction(const void* in, Type typeIn, string textIn, Namespace ns) { Action action=Action(new ConstGetAction(in, typeIn, textIn)); if (ns) { Action copier=ns->getCopier(typeIn); if (copier) action=branchAction(voidAction, copier, action); } return action; } ================================================ FILE: src/AllOperators.cpp ================================================ #include "../h/AllOperators.h" #include "../h/ErrorHandler.h" //shared_ptr ops(nullptr); AllOperators* ops=nullptr; Operator opCreate(string textIn, int leftPrecedenceIn, int rightPrecedenceIn, bool overloadableIn); void AllOperators::init() { //ops=shared_ptr(new AllOperators()); ops=new AllOperators(); } AllOperators::AllOperators() { #undef DECLARE_OP //#define DECLARE_OP(name, text, left, right, overload) putOpInMap(name); #define DECLARE_OP(name, text, prece, input, overload) putOpInMap(name); ALL_OPS; } void AllOperators::putOpInMap(Operator op) { opsMap[op->getText()]=op; } void AllOperators::get(string text, vector& out) { int start=0; int end=text.size(); while (startsecond); start=end; end=text.size(); break; } } } } bool AllOperators::isOpenBrac(Operator op) { return op==openPeren || op==openSqBrac || op==openCrBrac; } bool AllOperators::isCloseBrac(Operator op) { return op==closePeren || op==closeSqBrac || op==closeCrBrac; } /* // this is the only way to make an operator, and should only be called when setting up all the global operators at the top of Operator.cpp Operator opCreate(string textIn, int leftPrecedenceIn, int rightPrecedenceIn, bool overloadableIn) { Operator ptr(new OperatorData(textIn, leftPrecedenceIn, rightPrecedenceIn, overloadableIn)); for (auto i=OperatorData::precedenceLevels.begin();; i++) { if (i==OperatorData::precedenceLevels.end() || *i>leftPrecedenceIn) { OperatorData::precedenceLevels.insert(i, leftPrecedenceIn); break; } else if (*i==leftPrecedenceIn) break; } for (auto i=OperatorData::precedenceLevels.begin();; i++) { if (i==OperatorData::precedenceLevels.end() || *i>rightPrecedenceIn) { OperatorData::precedenceLevels.insert(i, rightPrecedenceIn); break; } else if (*i==rightPrecedenceIn) break; } //operators.push_back(ptr); return ptr; } */ ================================================ FILE: src/AstNode.cpp ================================================ #include "../h/AstNode.h" #include "../h/Namespace.h" #include "../h/ErrorHandler.h" #include "../h/msclStringFuncs.h" #include "../h/utils/stringDrawing.h" #include "../h/AllOperators.h" //AstNode astVoid=AstNode(new AstVoid); extern StackFrame stdLibStackFrame; extern Namespace globalNamespace; Action resolveLiteral(Token token); void AstNodeBase::copyToNode(AstNodeBase* other, bool copyCache) { other->inLeftType=inLeftType; other->inRightType=inRightType; other->nameHint=nameHint; if (copyCache) { other->action=action; other->returnType=returnType; other->dynamic=dynamic; other->ns=ns; other->inputHasBeenSet=inputHasBeenSet; } } /// List string AstList::getString() { vector data; for (int i=0; igetString()); } return str::makeList(data); } /*void AstList::resolveReturnType() { if (nodes.empty()) { returnType=Void; } else { nodes.back()->setInput(ns, dynamic, Void, Void); returnType=nodes.back()->getReturnType(); } }*/ void AstList::resolveAction() { if (!inLeftType->isVoid() || !inRightType->isVoid()) { throw PineconeError("AstList given non void input", INTERNAL_ERROR, getToken()); } ns=ns->makeChild(); for (int i=0; isetInput(ns, dynamic, Void, Void); nodes[i]->dealWithConstants(); } vector actions; for (int i=0; igetAction(); if (i!=(int)nodes.size()-1) action=ns->wrapInDestroyer(action); actions.push_back(action); } catch (PineconeError err) { err.log(); } } action=listAction(actions, *ns->getDestroyerActions()); } /// Function body string AstFuncBody::getString() { vector data; data.push_back("function"); vector types={leftTypeNode->getString(), rightTypeNode->getString(), returnTypeNode->getString()}; data.push_back(str::makeList(types)); data.push_back(bodyNode->getString()); return str::makeList(data); } AstNode AstFuncBody::makeCopyWithSpecificTypes(Type leftInType, Type rightInType) { Type leftWhatevType=leftTypeNode->getReturnType()->getSubType(); Type rightWhatevType=rightTypeNode->getReturnType()->getSubType(); if (!leftInType->matches(leftWhatevType) || !rightInType->matches(rightWhatevType)) { return nullptr; } AstNode actualLeftTypeNode; AstNode actualRightTypeNode; if (leftWhatevType->isWhatev()) { actualLeftTypeNode=AstTypeType::make(leftWhatevType->actuallyIs(leftInType)); } else { actualLeftTypeNode=leftTypeNode->makeCopy(false); } if (rightWhatevType->isWhatev()) { actualRightTypeNode=AstTypeType::make(rightWhatevType->actuallyIs(rightInType)); } else { actualRightTypeNode=rightTypeNode->makeCopy(false); } AstNode out=make(move(actualLeftTypeNode), move(actualRightTypeNode), returnTypeNode->makeCopy(false), bodyNode->makeCopy(false)); out->nameHint=nameHint; out->setInput(ns, dynamic, Void, Void); return out; } void AstFuncBody::resolveAction() { setTypesInput(); Namespace subNs=ns->makeChildAndFrame(nameHint.empty()?"unnamed function's namespace":nameHint+"'s namespace"); subNs->setInput(leftTypeNode->getReturnType()->getSubType(), rightTypeNode->getReturnType()->getSubType()); bodyNode->setInput(subNs, true, Void, Void); Type funcReturnType=returnTypeNode->getReturnType()->getSubType(); if (funcReturnType->isWhatev()) { funcReturnType=funcReturnType->actuallyIs(bodyNode->getReturnType()); } action=functionAction(bodyNode->makeCopy(true), funcReturnType, subNs->getStackFrame()); } /// Expression string AstExpression::getString() { string leftStr; if (!leftIn->isVoid()) { leftStr=leftIn->getString(); } string centerStr=center->getString(); string rightStr; if (!rightIn->isVoid()) { rightStr=rightIn->getString(); } return str::makeRootUpBinaryTree(centerStr, "", "", leftStr, rightStr); } void AstExpression::resolveAction() { if (!inLeftType->isVoid() || !inRightType->isVoid()) { throw PineconeError("AstExpression given non void input", INTERNAL_ERROR, getToken()); } if (rightIn->isType()) { throw PineconeError("types must be declared as constants", SOURCE_ERROR, rightIn->getToken()); } else if (center->isType() || center->isFunctionWithOutput() || leftIn->isType()) { throw PineconeError("a function implementation got into an expression node somehow", INTERNAL_ERROR, center->getToken()); } else { leftIn->setInput(ns, dynamic, Void, Void); rightIn->setInput(ns, dynamic, Void, Void); center->setInput(ns, dynamic, leftIn->getReturnType(), rightIn->getReturnType()); //error.log("left: " + leftIn->getString(), JSYK); //error.log("center: " + center->getString(), JSYK); //error.log("right: " + rightIn->getString(), JSYK); //error.log("", JSYK); action=branchAction(leftIn->getAction(), center->getAction(), rightIn->getAction()); } if (action->nameHint.empty()) action->nameHint=nameHint; } /// Const Expression string AstConstExpression::getString() { string centerStr=center->getString(); string rightStr; if (!rightIn->isVoid()) { rightStr=rightIn->getString(); } return str::makeRootUpBinaryTree(centerStr, "", "const", "", rightStr); } void AstConstExpression::resolveConstant() { if (!inLeftType->isVoid() || !inRightType->isVoid()) { throw PineconeError("AstConstExpression given non void input", INTERNAL_ERROR, getToken()); } //leftIn->setInput(ns, Void, Void); rightIn->setInput(ns, false, Void, Void); //error.log("resolveAction called for "+getString(), JSYK); ns->addNode(move(rightIn->makeCopy(true)), center->token->getText()); /* Action rightAction=rightIn->getAction(); void * val=rightAction->execute(nullptr, nullptr); //center->setInput(ns, false, Void, rightIn->getReturnType()); Action valAction=constGetAction(val, rightAction->getReturnType(), "const expression"); ns->addAction(valAction, center->token->getText());*/ } /// Operation with input string AstOpWithInput::getString() { string left; vector data; for (int i=0; igetString()); } if (data.size()==1) left=data[0]; else if (data.size()>1) left=str::makeList(data); data.clear(); string right; for (int i=0; igetString()); } if (data.size()==1) right=data[0]; else if (data.size()>1) right=str::makeList(data); return str::makeRootUpBinaryTree(str::putStringInTreeNodeBox(token->getText()), "", "", left, right); } void AstOpWithInput::resolveAction() { if (token->getOp()==ops->ifOp) { for (int i=0; isetInput(ns, dynamic, Void, Void); for (int i=0; isetInput(ns, dynamic, Void, Void); if (leftIn.empty()) { throw PineconeError("'?' must have a conditional to its left", SOURCE_ERROR, token); } else if (leftIn.size()!=1) { throw PineconeError("'?' can only have one conditional to its left", SOURCE_ERROR, token); } Action condition=leftIn[0]->getAction(); if (rightIn.empty()) { throw PineconeError("'?' must have a statement to its right", SOURCE_ERROR, token); } else if (rightIn.size()<=2) { Action a; try { a=rightIn[0]->getAction(); } catch (PineconeError err) { err.log(); a=voidAction; } if (rightIn.size()==1) { action=ifAction(condition, a); } else { Action e; try { e=rightIn[1]->getAction(); } catch (PineconeError err) { err.log(); e=voidAction; } action=ifElseAction(condition, a, e); } } else { throw PineconeError("'?' can only have 1 or 2 '|' seporated expressions to its right", SOURCE_ERROR, token); } } else if (token->getOp()==ops->loop) { bool usesSubNS=false; if (leftIn.size()==3) { ns=ns->makeChild(); usesSubNS=true; } for (int i=0; isetInput(ns, dynamic, Void, Void); for (int i=0; isetInput(ns, dynamic, Void, Void); Action initAction=nullptr, conditionAction, endAction, bodyAction; if (rightIn.size()>1) { throw PineconeError("'@' followed by multiple expressions", SOURCE_ERROR, token); } if (leftIn.size()==0) { throw PineconeError("condition needed before '@'", SOURCE_ERROR, token); } else if (leftIn.size()==1) { conditionAction=leftIn[0]->getAction(); endAction=voidAction; } else if (leftIn.size()==2) { conditionAction=leftIn[0]->getAction(); endAction=leftIn[1]->getAction(); } else if (leftIn.size()==3) { initAction=leftIn[0]->getAction(); conditionAction=leftIn[1]->getAction(); endAction=leftIn[2]->getAction(); } else { throw PineconeError("chain of length "+to_string(leftIn.size())+"preceding '@', it should be length 1-3", SOURCE_ERROR, token); } if (rightIn.empty()) { bodyAction=voidAction; } else { try { bodyAction=rightIn[0]->getAction(); } catch (PineconeError err) { err.log(); bodyAction=voidAction; } } vector actions; if (initAction) actions.push_back(ns->wrapInDestroyer(initAction)); actions.push_back(ns->wrapInDestroyer(loopAction(conditionAction, endAction, bodyAction))); action=usesSubNS ? action=listAction(actions, *ns->getDestroyerActions()) : action=listAction(actions, {}) ; } else if (token->getOp()==ops->andOp || token->getOp()==ops->orOp) { if (leftIn.size()>1 || rightIn.size()>1) { throw PineconeError("'"+token->getOp()->getText()+"' can not be sent '|' separated sequence", SOURCE_ERROR, getToken()); } if (leftIn.size()!=1 || rightIn.size()!=1) { throw PineconeError("'"+token->getOp()->getText()+"' must be given a left and right input", SOURCE_ERROR, getToken()); } leftIn[0]->setInput(ns, dynamic, Void, Void); rightIn[0]->setInput(ns, dynamic, Void, Void); Action leftAction=leftIn[0]->getAction(); Action rightAction=rightIn[0]->getAction(); if (leftAction->getReturnType()!=Bool) { throw PineconeError("'"+token->getOp()->getText()+"' can only be used with Bools", SOURCE_ERROR, leftIn[0]->getToken()); } if (rightAction->getReturnType()!=Bool) { throw PineconeError("'"+token->getOp()->getText()+"' can only be used with Bools", SOURCE_ERROR, rightIn[0]->getToken()); } if (token->getOp()==ops->andOp) { action=andAction(leftAction, rightAction); } else // (token->getOp()==ops->orOp) { action=orAction(leftAction, rightAction); } } else if (token->getOp()==ops->rightArrow) { throw PineconeError("AstOpWithInput::resolveAction called for token '"+token->getOp()->getText()+"', which it shouldn't have been", INTERNAL_ERROR, token); } else { throw PineconeError("AstOpWithInput made with bad token '"+token->getText()+"'", INTERNAL_ERROR, token); } } bool AstOpWithInput::isFunctionWithOutput() { return token->getOp()==ops->rightArrow && leftIn.size()==1 && rightIn.size()==1; } /// Token string AstToken::getString() { return str::putStringInTreeNodeBox(token->getText()); } void AstToken::resolveAction() { //error.log("resolveAction called for token "+token->getText(), JSYK, token); if (token->getType()==TokenData::IDENTIFIER || token->getType()==TokenData::OPERATOR) { if (token->getOp() && !token->getOp()->isOverloadable()) { throw PineconeError("non overloadable operator in AstToken, it should have been removed and processed by the parser", INTERNAL_ERROR, token); } /* if (inLeftType->getType()==TypeBase::TUPLE && inLeftType->getSubType(token->getText()).type!=nullptr) { if (inRightType->isVoid()) { action=getElemFromTupleAction(inLeftType, token->getText()); return; } else { throw PineconeError("sorry, Pinecone does not yet support mutating tuples", SOURCE_ERROR, token); } } */ action=ns->getActionForTokenWithInput(token, inLeftType, inRightType, dynamic, true, token); /* try { //error.log("looking for "+token->getText()+" in\n"+ns->getStringWithParents(), JSYK, token); action=ns->getActionForTokenWithInput(token, inLeftType, inRightType, dynamic); } catch (IdNotFoundError err) { if (token->getType()==TokenData::OPERATOR) { throw PineconeError("unknown overload for "+inLeftType->getString()+" "+token->getText()+" "+inRightType->getString()+"'", SOURCE_ERROR, token); } else if (token->getType()==TokenData::IDENTIFIER) { vector actions; ns->getActions(token->getText(), actions, dynamic); if (actions.size()>0) // if there are actions with the requested name that didn't match the type { string posibleInputs="the following is the inputs it can take:"; for (int i=0; i<(int)actions.size(); i++) { posibleInputs+="\n "; posibleInputs+=actions[i]->getInLeftType()->getString()+" . "+actions[i]->getInRightType()->getString()+" -> "+actions[i]->getReturnType()->getString(); } throw PineconeError( "'"+token->getText()+"' can not take input of type "+ inLeftType->getString()+" . "+inRightType->getString()+"\n"+ posibleInputs, SOURCE_ERROR, token); } } if (inRightType->getType()==TypeBase::METATYPE) { throw PineconeError("metatype handeling in "+FUNC+" not yet implemented", INTERNAL_ERROR, token); } else if (inRightType->isVoid()) { //throw PineconeError("unknown identifier '"+token->getText()+"' (var can not be made bc right in type is "+type->getString()+")", SOURCE_ERROR, token); throw PineconeError("unknown identifier '"+token->getText()+"'", SOURCE_ERROR, token); } else if (!inRightType->isCreatable()) { throw PineconeError("cannot create variable '"+token->getText()+"' of type {"+inRightType->getString()+"}", SOURCE_ERROR, token); } ns->addVar(inRightType, token->getText()); try { action=ns->getActionForTokenWithInput(token, inLeftType, inRightType, dynamic); } catch (IdNotFoundError err) { throw err.toPineconeError(token); } } */ } else if (token->getType()==TokenData::LITERAL || token->getType()==TokenData::STRING_LITERAL) { if (!inLeftType->isVoid() || !inRightType->isVoid()) { throw PineconeError("a literal can not be given an input", SOURCE_ERROR, token); } action=resolveLiteral(token); } else { throw PineconeError("AstToken givin invalid token '"+token->getText()+"' of type "+TokenData::typeToString(token->getType()), INTERNAL_ERROR, token); } } /// Tuple string AstTuple::getString() { vector data; for (int i=0; igetString()); } return str::makeList(data); } void AstTuple::resolveAction() { vector actions; for (int i=0; isetInput(ns, dynamic, Void, Void); actions.push_back(nodes[i]->getAction()); } action=makeTupleAction(actions); } /// TokenType string AstTokenType::getString() { return "{"+token->getText()+"}"; } void AstTokenType::resolveReturnType() { returnType=ns->getType(token->getText(), true, token)->getMeta(); } /// TupleType string AstTupleType::getString() { string out; out+="AstTupleType{"; for (int i=0; iname) { out+=type->name->getText()+": "; } out+=type->type->getString(); if (isetInput(ns, false, Void, Void); if (subTypes[i].name) { maker.add(subTypes[i].name->getText(), subTypes[i].type->getReturnType()->getSubType()); } else { maker.add(subTypes[i].type->getReturnType()->getSubType()); } } returnType=maker.get(true)->getMeta(); } ================================================ FILE: src/CppProgram.cpp ================================================ #include "../h/CppProgram.h" #include "../h/msclStringFuncs.h" #include #include string getValidCppId(string in) { string cpp; if (in.empty()) in="no_name"; int start=0; // all this should work with unicode (although it may inject random (but valid) characters into the C++ names) do { int i=start; for (; i='a' && in[i]<='z') || (in[i]>='A' && in[i]<='Z') || (in[i]>='0' && in[i]<='9') || in[i]=='_' ); i++) {} if (i!=start) { if (in[start]>='0' && in[start]<='9') cpp+="_"; cpp+=in.substr(start, i-start); } start=i+1; } while (start CppNameContainer::makeRoot() { auto out=shared_ptr(new CppNameContainer()); out->parent=nullptr; return out; } shared_ptr CppNameContainer::makeChild() { auto out=shared_ptr(new CppNameContainer()); children.push_back(out); out->parent=this; return out; } void CppNameContainer::addPn(const string& pn, const string& cppNameHint) { string validCppHint; if (cppNameHint=="<- the value of that pn string please") validCppHint=getValidCppId(pn); else if (!cppNameHint.empty()) validCppHint=getValidCppId(cppNameHint); if (pnToCppMap.find(pn)!=pnToCppMap.end()) { throw PineconeError("Tried to add '"+pn+"' as a pn name to a CppNameContainer but that pn name already exists", INTERNAL_ERROR); } // now we need to find a unique C++ name, the pinecone name will almost always be unique, but there may be cases where Pinecone treats scope differently or uses a keyword or something string cpp; if (validCppHint.empty()) { cpp=getUniqueString( "nm", [this](string in) -> bool { return !hasCpp(in); }, true ); } else { cpp=getUniqueString( validCppHint, [this](string in) -> bool { return !hasCpp(in); }, false ); } pnToCppMap[pn]=cpp; cppSet.insert(cpp); } void CppNameContainer::reserveCpp(const string& cpp, bool ignoreCollisions) { if (!ignoreCollisions && hasCpp(cpp)) { throw PineconeError("called CppNameContainer::reserveCpp with id '"+cpp+"', which already exists", INTERNAL_ERROR); } cppSet.insert(cpp); } bool CppNameContainer::hasPnMe(const string& pn) { return pnToCppMap.find(pn)!=pnToCppMap.end(); } bool CppNameContainer::hasPn(const string& pn) { return hasPnMe(pn) || (parent && parent->hasPn(pn)); } bool CppNameContainer::hasCpp(const string& cpp) { return hasCppMe(cpp) || hasCppUp(cpp) || hasCppDown(cpp); } bool CppNameContainer::hasCppMe(const string& cpp) { return cppSet.find(cpp)!=cppSet.end(); } bool CppNameContainer::hasCppUp(const string& cpp) { return parent && (parent->hasCppMe(cpp) || parent->hasCppUp(cpp)); } bool CppNameContainer::hasCppDown(const string& cpp) { for (auto i: children) { if (i->hasCppMe(cpp) || i->hasCppDown(cpp)) return true; } return false; } string CppNameContainer::getCpp(const string& pn) { auto result=pnToCppMap.find(pn); if (result==pnToCppMap.end()) { if (parent) return parent->getCpp(pn); else throw PineconeError("could not find C++ equivalent of '"+pn+"' in CppNameContainer::getCppForPn", INTERNAL_ERROR); } else return result->second; } /// funcs CppFuncBase::CppFuncBase(string prototypeIn, shared_ptr myNames, bool returnsValIn) { prototype=prototypeIn; namespaceStack.push_back(myNames); freshLine=true; returnsVal=returnsValIn; } void CppFuncBase::code(const string& in) { if (freshLine) { source+=indentString(in, indent, blockLevel); freshLine=(in.back()=='\n'); } else if (searchInString(in, "\n")>0) { source+="\n"+indentString(in, indent, blockLevel); if (in.back()!='\n') source+="\n"; freshLine=true; } else { source+=in; freshLine=(in.back()=='\n'); } } void CppFuncBase::name(const string& in) { code(namespaceStack.back()->getCpp(in)); } void CppFuncBase::line(const string& in) { code(in); endln(); } void CppFuncBase::endln() { if (exprLevel>0) { throw PineconeError("non zero expression level when ending line in C++ program, code so far:\n"+indentString(source), INTERNAL_ERROR); } else if (freshLine && (source.size()<2 || source[source.size()-2]==';' || source[source.size()-2]=='}' || source[source.size()-2]=='{')) { // do nothing //source+=indentString("\n", indent, blockLevel); } else { source+=";\n"; } freshLine=true; } void CppFuncBase::comment(const string& in) { if (searchInString(in, "\n")>=0) { source+=indentString("\n/*\n"+in+"\n*/\n", indent, blockLevel); freshLine=true; } else if (exprLevel>0 || !freshLine) { source+="/* "+in+" */"; freshLine=false; } else { source+=indentString("// "+in+"\n", indent, blockLevel); freshLine=true; } }; void CppFuncBase::pushExpr() { code("("); exprLevel++; freshLine=false; } void CppFuncBase::popExpr() { if (exprLevel<=0) { throw PineconeError("CppProgram::popExpression called with zero expressionLevel", INTERNAL_ERROR); } code(")"); exprLevel--; freshLine=false; } void CppFuncBase::pushBlock() { if (exprLevel>0) { throw PineconeError("CppProgram::pushBlock called when expressionLevel was not zero", INTERNAL_ERROR); } { code("{\n"); namespaceStack.push_back(namespaceStack.back()->makeChild()); blockLevel++; freshLine=true; } } void CppFuncBase::popBlock() { { if (blockLevel<=0) { throw PineconeError("CppProgram::popBlock called with zero indentationLevel", INTERNAL_ERROR); } blockLevel--; namespaceStack.pop_back(); code("}\n"); freshLine=true; } } string CppFuncBase::pnToCpp(const string& in) { return namespaceStack.back()->getCpp(in); } /// program CppProgram::CppProgram() { //funcs = unique_ptr>(new std::map()); globalNames=CppNameContainer::makeRoot(); setup(); } void CppProgram::setup() { globalTopCode+="// this C++ code is transpiled from Pinecone\n"; globalTopCode+="// Pinecone v"+to_string(VERSION_X)+"."+to_string(VERSION_Y)+"."+to_string(VERSION_Z)+" was used\n"; globalIncludesCode+="#include \n"; globalIncludesCode+="#include \n"; globalIncludesCode+="#include \n"; globalVarCode+="int argc = 0;\n"; globalVarCode+="char** argv = 0;\n"; vector cppReservedWords { // from C "auto", "const", "double", "float", "int", "short", "struct", "unsigned", "break", "continue", "else", "for", "long", "signed", "switch", "void", "case", "default", "enum", "goto", "register", "sizeof", "typedef", "volatile", "char", "do", "extern", "if", "return", "static", "union", "while", // from old C++ "asm", "dynamic_cast", "namespace", "reinterpret_cast", "try", "bool", "explicit", "new", "static_cast", "typeid", "catch", "false", "operator", "template", "typename", "class", "friend", "private", "this", "using", "const_cast", "inline", "public", "throw", "virtual", "delete", "mutable", "protected", "true", "wchar_t", // from C++11 "and", "bitand", "compl", "not_eq", "or_eq", "xor_eq", "and_eq", "bitor", "not", "or", "xor", // something else "endl", "INT_MIN", "std", "INT_MAX", "MAX_RAND", "NULL", // my custom "main", "argc", "argv", }; /* { "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "class", "const", "const_cast", "continue", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq", "operator", "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast", "return", "short", "signed", "sizeof", "static", "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq", "" }; */ for (auto i: cppReservedWords) { globalNames->reserveCpp(i); } pushFunc(string("_main"), Void, Void, Void); } string CppProgram::getTypeCode(Type in) { switch (in->getType()) { case TypeBase::VOID: return "void"; case TypeBase::DUB: return "double"; case TypeBase::INT: return "int"; case TypeBase::BYTE: return "unsigned char"; case TypeBase::BOOL: return "bool"; case TypeBase::PTR: if (in->getSubType()->isWhatev()) return "void *"; else return getTypeCode(in->getSubType())+" *"; case TypeBase::TUPLE: { if (in->getAllSubTypes()->size()==1) return getTypeCode((*in->getAllSubTypes())[0].type); string compact="{"+in->getCompactString()+"}"; if (!globalNames->hasPn(compact)) { globalNames->addPn(compact, in->nameHint); string code; code+="struct "; code+=globalNames->getCpp(compact); code+="\n{\n"; for (auto i: *in->getAllSubTypes()) { code+=indentString(getTypeCode(i.type)+" "+i.name+";\n", indent); } code+="\n"; code+=indentString(globalNames->getCpp(compact), indent); code+="() {}\n"; code+="\n"; auto conNames=globalNames->makeChild(); code+=indentString(globalNames->getCpp(compact), indent); code+="("; bool first=true; for (auto i: *in->getAllSubTypes()) { if (first) first=false; else code+=", "; conNames->addPn("+"+i.name+"_in"); code+=getTypeCode(i.type)+" "+conNames->getCpp("+"+i.name+"_in"); } code+=")\n"; code+=indentString("{\n", indent); for (auto i: *in->getAllSubTypes()) { code+=indentString(i.name+" = "+conNames->getCpp("+"+i.name+"_in")+";\n", indent, 2); } code+=indentString("}\n", indent); code+="};\n"; if (!globalTypesCode.empty()) globalTypesCode+="\n"; globalTypesCode+=code; } return globalNames->getCpp(compact); } default: throw PineconeError("CppProgram::getTypeCode called with invalid type "+(TypeBase::getString(in->getType())), INTERNAL_ERROR); } } void CppProgram::declareVar(const string& nameIn, Type typeIn, string initialValue) { /* CppNameContainer* names=&*activeFunc->namespaceStack.back(); while (names) { if (names->hasPnMe(nameIn)) return; names=names->getParent(); } */ if (isMain()) { declareGlobal(nameIn, typeIn, initialValue); return; } if (activeFunc->namespaceStack.back()->hasPn(nameIn)) { return; } activeFunc->namespaceStack.back()->addPn(nameIn); activeFunc->namespaceStack[0]->reserveCpp(pnToCpp(nameIn), true); activeFunc->varDeclareSource+=getTypeCode(typeIn)+" "+pnToCpp(nameIn); if (!initialValue.empty()) { activeFunc->varDeclareSource+=" = "+initialValue; } activeFunc->varDeclareSource+=";\n"; } void CppProgram::declareGlobal(const string& nameIn, Type typeIn, string initialValue) { if (globalNames->hasPn(nameIn)) return; string code; code+=getTypeCode(typeIn); code+=" "; globalNames->addPn(nameIn); code+=globalNames->getCpp(nameIn); if (!initialValue.empty()) { code+=" = "; code+=initialValue; } code+=";\n"; globalVarCode+=code; } void CppProgram::addHeadCode(const string& code) { globalIncludesCode += code + "\n"; } bool CppProgram::hasFunc(const string& name) { return funcs.find(name)!=funcs.end(); } void CppProgram::addFunc(const string& name, vector> args, string returnType, string contents) { if (hasFunc(name)) { throw PineconeError("called CppProgram::addFunc with function name '"+name+"', which already exists", INTERNAL_ERROR); } globalNames->addPn(name, name); string cppName=globalNames->getCpp(name); string prototype; prototype+=returnType; prototype+=" "+cppName+"("; for (int i=0; imakeChild(), (returnType!="" && returnType!="void"))); funcs[name]=func; func->code(contents); } //void CppProgram::pushFunc(const string& name, vector> args, Type returnType) void CppProgram::pushFunc(const string& name, const string& cppNameHint, Type leftIn, Type rightIn, Type returnType) { if (hasFunc(name)) { throw PineconeError("called CppProgram::pushFunc with function name '"+name+"', which already exists", INTERNAL_ERROR); } globalNames->addPn(name, cppNameHint); string cppName=globalNames->getCpp(name); auto funcNames=globalNames->makeChild(); string prototype; prototype+=getTypeCode(returnType); prototype+=" "+cppName+"("; bool keepTuplesTogether=true; if (keepTuplesTogether) { if (leftIn->isCreatable()) { prototype+=getTypeCode(leftIn)+" me"; funcNames->addPn("me"); } if (rightIn->isCreatable()) { if (leftIn->isCreatable()) prototype+=", "; prototype+=getTypeCode(rightIn)+" in"; funcNames->addPn("in"); } } else { vector> args; if (leftIn->getType()==TypeBase::TUPLE) { for (auto i: *leftIn->getAllSubTypes()) { args.push_back({getTypeCode(i.type), i.name}); } } else if (!leftIn->isCreatable()) { // do nothing } else { args.push_back({getTypeCode(leftIn), "me"}); } if (rightIn->getType()==TypeBase::TUPLE) { for (auto i: *rightIn->getAllSubTypes()) { args.push_back({getTypeCode(i.type), i.name}); } } else if (!rightIn->isCreatable()) { // do nothing } else { args.push_back({getTypeCode(rightIn), "in"}); } for (int i=0; iaddPn(args[i].second, args[i].second); prototype+=funcNames->getCpp(args[i].second); } } prototype+=")"; activeFunc=CppFunc(new CppFuncBase(prototype, funcNames, returnType->isCreatable())); funcs[name]=activeFunc; funcStack.push_back(name); } void CppProgram::popFunc() { if (activeFunc->getExprLevel()>0 || !activeFunc->getIfFreshLine() || activeFunc->getBlockLevel()>0) { throw PineconeError("called CppProgram::popFunc when function wasn't ready", INTERNAL_ERROR); } funcStack.pop_back(); if (funcStack.empty()) { throw PineconeError("called CppProgram::popFunc too many times", INTERNAL_ERROR); } activeFunc=funcs[funcStack.back()]; } string CppProgram::getCppCode() { string out; if (!globalTopCode.empty()) out+=globalTopCode+"\n"; if (!globalIncludesCode.empty()) out+=globalIncludesCode+"\n"; if (!globalTypesCode.empty()) out+=globalTypesCode+"\n"; if (funcs.size()>1) { for (auto i: funcs) { if (i.first!="main") out+=i.second->getPrototype()+";\n"; } out+="\n"; } if (!globalVarCode.empty()) out+=globalVarCode+"\n"; for (auto i: funcs) { out+=i.second->getPrototype(); string funcSrc=i.second->getSource(); if (funcSrc.size()<2) { out+="\n{\n\t// empty function\n}\n\n"; } else { if (i.second->getIfReturnsVal() && funcSrc[0]!='{' && searchInString(funcSrc, ";")==int(funcSrc.size())-2) funcSrc="return "+funcSrc; out+="\n{\n"; if (!i.second->varDeclareSource.empty()) out+=indentString(i.second->varDeclareSource+"\n", indent); out+=indentString(funcSrc, indent); if (!out.empty() && out.back()!='\n') out+=";\n"; //if (i.first=="main") // out+=indentString("return 0;\n", indent); //if (out.substr(out.size()-2, 2)!=";\n") // out+=";\n"; out+="}\n\n"; } } out+= "int main(int argcIn, char** argvIn)\n\ {\n\ argc = argcIn;\n\ argv = argvIn;\n\ if (argc >= 2 && strcmp(argv[1], \"--running-from-pinecone\") == 0)\n\ {\n\ argc -= 2;\n\ if (argc == 0)\n\ argv = 0;\n\ else\n\ argv += 2;\n\ }\n\ _main();\n\ return 0;\n\ }\n"; return out; } ================================================ FILE: src/ErrorHandler.cpp ================================================ #include "../h/ErrorHandler.h" #include "../h/msclStringFuncs.h" #include "../h/SourceFile.h" #include using std::cout; using std::endl; ErrorHandler error; string ErrorHandler::priorityToStr(ErrorPriority in) { switch (in) { case SOURCE_ERROR: return "error"; break; case SOURCE_WARNING: return "warning"; break; case JSYK: return "jsyk"; break; case INTERNAL_ERROR: return "INTERNAL ERROR"; break; case RUNTIME_ERROR: return "runtime error"; break; default: return "UNKNOWN PRIORITY LEVEL"; break; } } void ErrorHandler::log(string msg, ErrorPriority priority, Token token) { if (priority==SOURCE_ERROR || priority==INTERNAL_ERROR || priority==RUNTIME_ERROR) errorHasBeenLogged=true; // gcc style //if (token) // cout << token->getFile() << ":" << token->getLine() << ":" << token->getCharPos() << ": "; //cout << priorityToStr(priority) << ": " << msg << endl; // Pinecone style cout << priorityToStr(priority); if (token) { cout << " in '" << token->getFile()->getFilename() << "' on line " << token->getLine() << ":" << endl; cout << indentString(msg, " ") << endl; string line=token->getFile()->getLine(token->getLine()); int wspace=0; for (; wspacegetCharPos()-1-wspace; i++) arrows+=" "; for (int i=0; igetText().size()); i++) arrows+="^"; cout << indentString(""+line.substr(wspace, string::npos)+"\n"+arrows, " ") << endl; } else { cout << ": " << msg << endl; } } void ErrorHandler::msg(string in) { cout << "message: " << in << endl; } PineconeError::PineconeError(string msgIn, ErrorPriority priorityIn, Token tokenIn) { msg=msgIn; priority=priorityIn; token=tokenIn; } void PineconeError::log() { error.log(msg, priority, token); } ================================================ FILE: src/Lexer.cpp ================================================ #include "../h/Token.h" #include "../h/ErrorHandler.h" #include "../h/Operator.h" #include "../h/AllOperators.h" #include "../h/SourceFile.h" #include using std::vector; #include using std::unordered_map; class CharClassifier { public: enum Type { WHITESPACE, LINE_BREAK, NEWLINE, LETTER, DIGIT, OPERATOR, STRING_QUOTE, SINGLE_LINE_COMMENT, MULTI_LINE_COMMENT_START, MULTI_LINE_COMMENT_END, UNKNOWN, }; static inline TokenData::Type getTokenType(CharClassifier::Type type, TokenData::Type previousType); inline CharClassifier::Type get(shared_ptr file, int i); private: void setUp(); private: unordered_map hm; bool hasSetUp=false; }; CharClassifier charClassifier; void CharClassifier::setUp() { hm[' ']=WHITESPACE; hm['\t']=WHITESPACE; hm['\r']=WHITESPACE; // ignore '\r' character on windows style line ending hm['\n']=NEWLINE; hm[';']=LINE_BREAK; for (char c='a'; c<='z'; ++c) hm[c]=LETTER; for (char c='A'; c<='Z'; ++c) hm[c]=LETTER; hm['_']=LETTER; for (char c='0'; c<='9'; ++c) hm[c]=DIGIT; hm['#']=SINGLE_LINE_COMMENT; hm['"']=STRING_QUOTE; unordered_map& opsMap=ops->getOpsMap(); for (auto i=opsMap.begin(); i!=opsMap.end(); ++i) { string str=(*i).first; for (unsigned j=0; j file, int index) { // set up the first time this function is called if (!hasSetUp) setUp(); // chack fo multi line comments in a special way, because they are multi character switch ((*file)[index]) { case '/': if (indexsize())-1 && (*file)[index+1]=='/') return MULTI_LINE_COMMENT_START; break; case '\\': if (index>0 && (*file)[index-1]=='\\') return MULTI_LINE_COMMENT_END; break; case '.': // allow a . to be a digit character only if it is followed by a digit if (indexsize())-1) { auto i=hm.find((*file)[index+1]); if (i!=hm.end() && i->second==DIGIT) return DIGIT; } break; } // handle all other cases using the hashmap char c=(*file)[index]; auto i=hm.find(c); if (i==hm.end()) return UNKNOWN; else return i->second; } inline TokenData::Type CharClassifier::getTokenType(CharClassifier::Type type, TokenData::Type previousType) { if (previousType==TokenData::LINE_COMMENT) { if (type==NEWLINE) return TokenData::WHITESPACE; else return TokenData::LINE_COMMENT; } else if (previousType==TokenData::BLOCK_COMMENT) { if (type==MULTI_LINE_COMMENT_END) return TokenData::WHITESPACE; else return TokenData::BLOCK_COMMENT; } else if (previousType==TokenData::STRING_LITERAL) { if (type==STRING_QUOTE) return TokenData::WHITESPACE; else return TokenData::STRING_LITERAL; } switch (type) { case SINGLE_LINE_COMMENT: return TokenData::LINE_COMMENT; case MULTI_LINE_COMMENT_START: return TokenData::BLOCK_COMMENT; case MULTI_LINE_COMMENT_END: error.log("block comment end without start", SOURCE_ERROR); return TokenData::UNKNOWN; case WHITESPACE: return TokenData::WHITESPACE; case LINE_BREAK: case NEWLINE: return TokenData::LINE_END; case OPERATOR: return TokenData::OPERATOR; case LETTER: case DIGIT: if (previousType==TokenData::IDENTIFIER || previousType==TokenData::LITERAL) return previousType; else if (type==DIGIT) return TokenData::LITERAL; else return TokenData::IDENTIFIER; case STRING_QUOTE: return TokenData::STRING_LITERAL; default: return TokenData::UNKNOWN; } } void lexString(shared_ptr file, vector& tokens) { string tokenTxt; int line=1; int charPos=1; TokenData::Type type=TokenData::WHITESPACE; for (int i=0; isize(); i++) { CharClassifier::Type charType=charClassifier.get(file, i); TokenData::Type newType=CharClassifier::getTokenType(charType, type); if (newType!=type) { if (!tokenTxt.empty()) { if (type==TokenData::OPERATOR) { vector opMatches; ops->get(tokenTxt, opMatches); for (auto op: opMatches) { tokens.push_back(makeToken(op->getText(), file, line, charPos-tokenTxt.size(), type, op)); } } else if (type==TokenData::LINE_COMMENT || type==TokenData::BLOCK_COMMENT) { // do nothing } else { Token token=makeToken(tokenTxt, file, line, charPos-tokenTxt.size(), type); if (type==TokenData::UNKNOWN) { PineconeError("invalid token '"+tokenTxt+"'", SOURCE_ERROR, token).log(); } else { tokens.push_back(token); } } } tokenTxt=""; } if (newType!=TokenData::WHITESPACE && newType!=TokenData::LINE_END) { if (newType==TokenData::STRING_LITERAL && (*file)[i]=='\\') { i++; if ((*file)[i]=='n') tokenTxt+='\n'; else if ((*file)[i]=='"') tokenTxt+='"'; else if ((*file)[i]=='t') tokenTxt+='\t'; else if ((*file)[i]=='\\') tokenTxt+='\\'; else throw PineconeError(string()+"invalid escape character '\\"+(*file)[i]+"'", SOURCE_ERROR, makeToken(tokenTxt+(*file)[i], file, line, charPos-tokenTxt.size(), type)); } else { tokenTxt+=(*file)[i]; } } type=newType; if ((*file)[i]=='\n') { line++; charPos=1; } else { charPos++; } } } ================================================ FILE: src/Namespace.cpp ================================================ #include "../h/Namespace.h" #include "../h/StackFrame.h" #include "../h/msclStringFuncs.h" #include "../h/ErrorHandler.h" #include "../h/utils/stringNumConversion.h" void NamespaceData::IdMap::add(string key, AstNode node) { auto i=nodes.find(key); if (i==nodes.end()) { nodes[key]=vector(); } nodes[key].push_back(move(node)); } void NamespaceData::IdMap::get(string key, vector& out) { auto matches=nodes.find(key); if (matches!=nodes.end()) { for (unsigned i=0; isecond.size(); i++) { out.push_back(&*matches->second[i]); } } } Namespace NamespaceData::makeRootNamespace() { return Namespace(new NamespaceData(Namespace(nullptr), shared_ptr(new StackFrame()), "root")); } Namespace NamespaceData::makeChild() { return Namespace(new NamespaceData(shared_from_this(), stackFrame)); } Namespace NamespaceData::makeChildAndFrame(string nameIn) { return Namespace(new NamespaceData(shared_from_this(), shared_ptr(new StackFrame()), nameIn)); } NamespaceData::NamespaceData(Namespace parentIn, shared_ptr stackFrameIn, string nameIn) { parent=parentIn; stackFrame=stackFrameIn; myName=nameIn; } string NamespaceData::getString() { /*string out; out+="normal functions:\n"; for (auto i: actions) { out+="\t"; out+=i.first; if (i.second.size()>1) out+=" (" + to_string(i.second.size()) + " overloads)"; out+="\n"; } out+="\nconverters:\n"; for (auto i: converters) { out+="\t"; out+=i.first->getString(); if (i.second.size()>1) out+=" (" + to_string(i.second.size()) + " overloads)"; out+="\n"; } out+="\noperators:\n"; for (auto i: operators) { out+="\t"; out+=i.first->getText(); if (i.second.size()>1) out+=" (" + to_string(i.second.size()) + " overloads)"; out+="\n"; } out+="\ntypes:\n"; for (auto i: types) { out+="\t"; out+=i.second->getString() + " (" + i.second->getString() + ")"; out+="\n"; } return out;*/ return "NamespaceData::getString not yet implemented"; } string NamespaceData::getStringWithParents() { auto ptr=shared_from_this(); string out; while (ptr) { out=putStringInBox(ptr->getString()+"\n"+out, ptr->myName); ptr=ptr->parent; } return out; } void NamespaceData::setInput(Type left, Type right) { if (parent && parent->getStackFrame()==stackFrame) { error.log("called "+FUNC+" on namespace that is not the root of a stack frame, thus it can not get input", INTERNAL_ERROR); return; } stackFrame->setInput(left, right); //if (!left->isVoid()) if (left->isCreatable()) { string leftName="me"; size_t leftOffset=stackFrame->getLeftOffset(); Action leftGetAction=varGetAction(leftOffset, left, leftName); Action leftSetAction=varSetAction(leftOffset, left, leftName); addNode(AstActionWrapper::make(leftGetAction), leftName); addNode(AstActionWrapper::make(leftSetAction), leftName); } //if (!right->isVoid()) if (right->isCreatable()) { string rightName="in"; size_t rightOffset=stackFrame->getRightOffset(); Action rightGetAction=varGetAction(rightOffset, right, rightName); Action rightSetAction=varSetAction(rightOffset, right, rightName); addNode(AstActionWrapper::make(rightGetAction), rightName); addNode(AstActionWrapper::make(rightSetAction), rightName); } } Action NamespaceData::addVar(Type type, string name) { size_t offset=stackFrame->getSize(); stackFrame->addMember(type); Action getAction; Action setAction; Action copyAction; Namespace top=shared_from_this(); while(top->parent) { top=top->parent; } if (stackFrame!=top->stackFrame) { getAction=varGetAction(offset, type, name); setAction=varSetAction(offset, type, name); } else { getAction=globalGetAction(offset, type, name); setAction=globalSetAction(offset, type, name); } copyAction=getCopier(type); if (copyAction) { dynamicActions.add(name, AstActionWrapper::make(branchAction(voidAction, copyAction, getAction))); } else { dynamicActions.add(name, AstActionWrapper::make(getAction)); } dynamicActions.add(name, AstActionWrapper::make(setAction)); Action destructor=getDestroyer(type); if (destructor) { destructorActions.push_back(branchAction(voidAction, destructor, getAction)); } return setAction; } void NamespaceData::addNode(AstNode node, string id) { if (node->nameHint.empty()) { node->nameHint=id; node->nameHintSet(); } if (node->isType()) { types.add(id, move(node)); } /* else if (id=="__destroy__") { Action action=node->getAction(); if (action->getInRightType()->isVoid() || !action->getInLeftType()->isVoid() || !action->getReturnType()->isVoid()) { throw PineconeError("incorrect type signiture for destroyer", SOURCE_ERROR, node->getToken()); } destructors.add(str::ptrToUniqueStr(&*action->getInRightType(), 6), move(node)); } else if (id=="__copy__") { Action action=node->getAction(); if (action->getInRightType()->isVoid() || !action->getInLeftType()->isVoid() || action->getReturnType()!=action->getInRightType()) { throw PineconeError("incorrect type signiture for copier", SOURCE_ERROR, node->getToken()); } copiers.add(str::ptrToUniqueStr(&*action->getReturnType(), 6), move(node)); } */ else { // if the left or the right is a Whatev and the types match up if (node->canBeWhatev()) { whatevActions.add(id, move(node)); } else { actions.add(id, move(node)); } } } Type NamespaceData::getType(string name, bool throwSourceError, Token tokenForError) { vector results; types.get(name, results); if (results.empty()) { if (parent) return parent->getType(name, throwSourceError, tokenForError); else if (throwSourceError) throw PineconeError("'"+name+"' type not found", SOURCE_ERROR); else return nullptr; } else if (results.size()!=1) { throw PineconeError("namespace has multiple defenitions of the same type '"+name+"'", INTERNAL_ERROR); } else if (results[0]->getReturnType()->getType()!=TypeBase::METATYPE) { throw PineconeError("node returning non meta type stored in namespace type map for type '"+name+"'", INTERNAL_ERROR); } else { return results[0]->getReturnType()->getSubType(); } } Action NamespaceData::getDestroyer(Type type) { return getActionForTokenWithInput(makeToken("__destroy__"), Void, type, false, false, nullptr); /* vector nodes; destructors.get(str::ptrToUniqueStr(&*type, 6), nodes); if (nodes.empty()) { if (parent) return parent->getDestroyer(type); else return nullptr; } else if (nodes.size()>1) { throw PineconeError("multiple destroyers for a single type in a single namespace", INTERNAL_ERROR); } else { return nodes[0]->getAction(); } */ } Action NamespaceData::wrapInDestroyer(Action in) { Action destroyer=getDestroyer(in->getReturnType()); return destroyer ? branchAction(voidAction, destroyer, in) : in ; } Action NamespaceData::getCopier(Type type) { return getActionForTokenWithInput(makeToken("__copy__"), Void, type, false, false, nullptr); /* vector nodes; copiers.get(str::ptrToUniqueStr(&*type, 6), nodes); if (nodes.empty()) { if (parent) return parent->getCopier(type); else return nullptr; } else if (nodes.size()>1) { throw PineconeError("multiple copiers for a single type in a single namespace", INTERNAL_ERROR); } else { return nodes[0]->getAction(); } */ } Action NamespaceData::getActionForTokenWithInput(Token token, Type left, Type right, bool dynamic, bool throwSourceError, Token tokenForError) { vector matches; vector nodes; bool foundNodes=false; AstNode tupleNode; // this is needed for memory management, so if a tuple node is needed it can be kept around until the function exits string searchText = (token->getOp() ? token->getOp()->getText() : token->getText()); getNodes(nodes, searchText, true, dynamic, false); if (left->getType()==TypeBase::TUPLE && token->getType()==TokenData::IDENTIFIER) { auto match=left->getSubType(searchText); if (match.type) { if (right->isVoid()) { tupleNode=AstActionWrapper::make(getElemFromTupleAction(left, searchText)); nodes.push_back(&*tupleNode); } } } if (!nodes.empty()) foundNodes=true; nodesToMatchingActions(matches, nodes, left, right); if (!matches.empty()) { if (matches.size() == 1) { return matches[0]; } else if (throwSourceError) { throw PineconeError("multiple matching instances of '"+token->getText()+"' found", SOURCE_ERROR, tokenForError); } else { return nullptr; } } nodes.clear(); getNodes(nodes, searchText, false, false, true); if (!nodes.empty()) foundNodes=true; for (auto i: nodes) { AstNode instance=i->makeCopyWithSpecificTypes(left, right); if (instance) { matches.push_back(instance->getAction()); actions.add(token->getText(), move(instance)); } } if (!matches.empty()) { if (matches.size() == 1) { return matches[0]; } else if (throwSourceError) { throw PineconeError("multiple whatev instances of '"+token->getText()+"' found", SOURCE_ERROR, tokenForError); } else { return nullptr; } } if (!foundNodes && dynamic && token->getType()==TokenData::IDENTIFIER && left->isVoid() && right->isCreatable()) { return addVar(right, token->getText()); } if (throwSourceError) { if (foundNodes) throw PineconeError("correct overload of '"+token->getText()+"' not found for types "+left->getString()+" and "+right->getString(), SOURCE_ERROR, tokenForError); else throw PineconeError("'"+token->getText()+"' not found", SOURCE_ERROR, tokenForError); } else { return nullptr; } } void NamespaceData::getNodes(vector& out, string text, bool checkActions, bool checkDynamic, bool checkWhatev) { if (checkActions) { actions.get(text, out); } if (checkDynamic) { dynamicActions.get(text, out); } if (checkWhatev) { whatevActions.get(text, out); } if (parent) parent->getNodes(out, text, checkActions, checkDynamic, checkWhatev); } void NamespaceData::nodesToMatchingActions(vector& out, vector& nodes, Type leftInType, Type rightInType) { for (auto i: nodes) { Action action=i->getAction(); if (action->getInLeftType()->matches(leftInType) && action->getInRightType()->matches(rightInType)) out.push_back(action); //Action converter=getConverter(action, leftInType, rightInType); } } ================================================ FILE: src/Parser.cpp ================================================ #include "../h/Token.h" #include "../h/AstNode.h" #include "../h/Namespace.h" #include "../h/ErrorHandler.h" #include "../h/StackFrame.h" #include "../h/AllOperators.h" #include "../h/msclStringFuncs.h" #include "../h/AstNode.h" #include using std::vector; #include using std::cout; using std::endl; using std::min; using std::max; using std::pair; void lexString(shared_ptr file, vector& tokens); // unless otherwise noted, these are what the perams for the following functions mean // tokens: the tokens to parse // table: the table to use // left: left most token to parse (inclusive) // right: right most token to parse (inclusive) // returns: (if type is AstNode) the action pointer for that section of the program // splits a stream of tokens into a ListAstNode and calls parseExpression on each expression void parseTokenList(const vector& tokens, int left, int right, vector& nodes); int findExpressionSplit(const vector& tokens, int left, int right); // recursivly parses a single expression (no action lists) AstNode parseExpression(const vector& tokens, int left, int right); // returns the index of the closing brace that matches the given opening brace index, works with (), [], and {} // tokens: the token array to use // start: the index of an open peren // returns: the index of the close peren that matches int skipBrace(const vector& tokens, int start); void parseSequence(const vector& tokens, int left, int right, Operator splitter, vector& out); AstNode parseOperator(const vector& tokens, int left, int right, int index); //AstNode parseLiteral(Token token); unique_ptr parseType(const vector& tokens, int left, int right); //AstNode parseSingleTypeElement(const vector& tokens, int& i, int right, string& name, Type& type); //Type parseTypeToken(Token token); void importFile(vector& nodes, string path); //AstNode parseIdentifier(Token token, AstNode leftIn, AstNode rightIn); //void parseIdentifierConst(Token token, AstNode rightIn); AstNode astNodeFromTokens(const vector& tokens, int left, int right) { vector nodes; parseTokenList(tokens, left, right, nodes); if (nodes.size()==0) { return AstVoid::make(); } else if (nodes.size()==1) { return move(nodes[0]); } else { return AstList::make(nodes); } } int skipBrace(const vector& tokens, int start) { Operator open, close; int step; Operator op=tokens[start]->getOp(); if (tokens[start]->getOp()==ops->openPeren) { open=ops->openPeren; close=ops->closePeren; step=1; } else if (tokens[start]->getOp()==ops->closePeren) { open=ops->closePeren; close=ops->openPeren; step=-1; } else if (tokens[start]->getOp()==ops->openSqBrac) { open=ops->openSqBrac; close=ops->closeSqBrac; step=1; } else if (tokens[start]->getOp()==ops->closeSqBrac) { open=ops->closeSqBrac; close=ops->openSqBrac; step=-1; } else if (tokens[start]->getOp()==ops->openCrBrac) { open=ops->openCrBrac; close=ops->closeCrBrac; step=1; } else if (tokens[start]->getOp()==ops->closeCrBrac) { open=ops->closeCrBrac; close=ops->openCrBrac; step=-1; } else { throw PineconeError(FUNC + " called with index that is not a valid brace", INTERNAL_ERROR, tokens[start]); } int c=1; int i=start; while(true) { i+=step; if (i>=int(tokens.size())) { throw PineconeError("no matching brace", SOURCE_ERROR, tokens[start]); } if (tokens[i]->getOp()==open) { c++; } else if (tokens[i]->getOp()==close) { c--; if (c<=0) { return i; } } } } /*AstNode parseExpression(const vector& tokens, int left, int right) { //error.log("parsing expression: "+stringFromTokens(tokens, left, right), JSYK); if (left>right) { throw PineconeError(FUNC + " sent left higher then right", INTERNAL_ERROR, tokens[left]); } else if (left==right) { return AstToken::make(tokens[left]); } vector isMinLeft(right-left+1); vector isMinRight(right-left+1); int lowest; lowest=10000; //an number bigger then any precedence for (int i=left; i<=right; i++) { isMinLeft[i]=false; isMinRight[i]=false; } for (int i=left; i<=right; i++) { Operator op=tokens[i]->getOp(); if (op) { if (op==ops->openPeren || op==ops->openSqBrac || op==ops->openCrBrac) { int j=i; i=skipBrace(tokens, i); if (j==left && i==right) { if (op==ops->openPeren) { if (left+1openCrBrac) { return parseType(tokens, left+1, right-1); } else { throw PineconeError("unknown bracket '"+op->getText()+"'", INTERNAL_ERROR); } } } else { if (op->getLeftPrece()getRightPrece()); } } lowest=10000; //an number bigger then any precedence for (int i=right; i>=left; i--) { Operator op=tokens[i]->getOp(); if (op) { if (op==ops->closePeren || op==ops->closeSqBrac || op==ops->closeCrBrac) { i=skipBrace(tokens, i); } else { if (op->getRightPrece()getLeftPrece()); } } } for (int i=left; i<=right; i++) { if (isMinLeft[i] && isMinRight[i]) { AstNode leftNode=parseExpression(tokens, left, i-1); AstNode rightNode=parseExpression(tokens, i+1, right); if (tokens[i]->getOp()==ops->colon || tokens[i]->getOp()==ops->doubleColon) { return AstExpression::make(AstVoid::make(), move(leftNode), move(rightNode)); } else { return AstExpression::make(move(leftNode), AstToken::make(tokens[i]), move(rightNode)); } } } throw PineconeError("could not find where to split expression '" + stringFromTokens(tokens, left, right) + "'", SOURCE_ERROR, tokens[left]); //error.log("range: " + ([&]()->string{string out; for (int i=left; i<=right; i++) {out+=tokens[i]->getText()+" ";} return out;})(), JSYK, tokens[left]); //error.log("isMin: " + ([&]()->string{string out; for (auto i: isMin) {out+="\n"+to_string(i.first)+", "+to_string(i.second);} return out;})(), JSYK, tokens[left]); } */ int findExpressionSplit(const vector& tokens, int left, int right) { int minPrece=-1; int indexOfMin=-1; for (int i=left; i<=right; i++) { //cout << "looking at " << tokens[i]->getText() << endl; /*if (tokens[i]->getOp()) { //cout << "precedence: " << tokens[i]->getOp()->getPrecedence() } */ if (ops->isOpenBrac(tokens[i]->getOp())) { int j=skipBrace(tokens, i); i=j; // i is now the close brace, and after it is incremented it will be the token after that } else if (tokens[i]->getOp() && (minPrece<0 || tokens[i]->getOp()->getPrecedence()getOp()->getPrecedence(); indexOfMin=i; // this ensures that if the precedence is divisable by 2, the same precedence again will replace this one as the min // it is my way of making even precedences be right associative and odd ones be left associative if (minPrece%2) { minPrece++; } } } if (indexOfMin<0) { throw PineconeError(FUNC+" could not find operator to split expression", INTERNAL_ERROR, tokens[left]); } return indexOfMin; } AstNode parseExpression(const vector& tokens, int left, int right) { //error.log(FUNC+" called on '"+stringFromTokens(tokens, left, right)+"'", JSYK); if (left>right) { throw PineconeError(FUNC + " sent left higher then right", INTERNAL_ERROR, tokens[left]); } else if (left==right) { return AstToken::make(tokens[left]); } if (ops->isOpenBrac(tokens[left]->getOp()) && skipBrace(tokens, left)==right) // if the braces enclose this entire expression { if (tokens[left]->getOp()==ops->openPeren) { return astNodeFromTokens(tokens, left+1, right-1); } else if (tokens[left]->getOp()==ops->openCrBrac) { return parseType(tokens, left+1, right-1); } else { throw PineconeError("unhandled brace '"+tokens[left]->getOp()->getText()+"'", INTERNAL_ERROR, tokens[left]); } } int i=findExpressionSplit(tokens, left, right); Operator op=tokens[i]->getOp(); if (op==ops->minus && i==left && i!=right) { //dont error } else if ((i==left)==op->takesLeftInput() || (i==right)==op->takesRightInput()) { throw PineconeError("improper use of '"+op->getText()+"' operator", SOURCE_ERROR, tokens[i]); } if (op==ops->pipe) { throw PineconeError("invalid use of '"+op->getText()+"'", SOURCE_ERROR, tokens[i]); } else if (op==ops->ifOp || op==ops->loop || op==ops->rightArrow || op==ops->andOp || op==ops->orOp) { vector leftNodes; vector rightNodes; if (i>left) parseSequence(tokens, left, i-1, ops->pipe, leftNodes); if (ipipe, rightNodes); return AstOpWithInput::make(leftNodes, tokens[i], rightNodes); } else if (op==ops->comma) { vector nodes; parseSequence(tokens, left, right, ops->comma, nodes); return AstTuple::make(nodes); } else if (op==ops->doubleColon) { unique_ptr centerNode=nullptr; AstNode rightNode=nullptr; if (i==left+1 && tokens[i-1]->getType()==TokenData::IDENTIFIER) { centerNode=AstToken::make(tokens[i-1]); } else { throw PineconeError("you can only use constant assignment on a single identifier", SOURCE_ERROR, tokens[i]); } if (inotEqual) { AstNode rightNode=ileft?parseExpression(tokens, left, i-1):AstVoid::make(); AstNode centerNode=AstToken::make( makeToken( ops->equal->getText(), tokens[i]->getFile(), tokens[i]->getLine(), tokens[i]->getCharPos()+1, TokenData::OPERATOR, ops->equal ) ); AstNode notNode=AstToken::make( makeToken( ops->notOp->getText(), tokens[i]->getFile(), tokens[i]->getLine(), tokens[i]->getCharPos(), TokenData::OPERATOR, ops->notOp ) ); return AstExpression::make( AstVoid::make(), move(notNode), AstExpression::make( move(leftNode), move(centerNode), move(rightNode) ) ); } else if (op==ops->plusPlus || op==ops->minusMinus) { throw PineconeError("++ and -- are not yet implemented", SOURCE_ERROR, tokens[i]); //AstNode leftNode=parseExpression(tokens, left, i-1); //return AstExpression::make(AstVoid::make(), leftNode, AstExpression::make(leftNode, AstToken::make(Token))) } else if (op==ops->dot) { return AstExpression::make ( i>left ? parseExpression(tokens, left, i-1) : AstVoid::make(), icolon) { AstNode leftNode=AstVoid::make(); AstNode centerNode=parseExpression(tokens, left, i-1);; AstNode rightNode=ileftIn->isVoid() && !exprNode->center->isVoid() && exprNode->rightIn->isVoid()) { leftNode=move(exprNode->leftIn); centerNode=move(exprNode->center); } } // make a function body if needed, else make a normal expression if ( (centerNode->isType() || centerNode->isFunctionWithOutput()) && (leftNode->isVoid() || leftNode->isType()) && !rightNode->isType() ){ if (leftNode->isVoid()) leftNode=AstVoidType::make(); AstNode funcRightIn; AstNode funcReturn; if (centerNode->isFunctionWithOutput()) { funcRightIn=move(((AstOpWithInput*)&*(centerNode))->leftIn[0]); funcReturn=move(((AstOpWithInput*)&*(centerNode))->rightIn[0]); } else { funcRightIn=move(centerNode); funcReturn=AstVoidType::make(); } return AstFuncBody::make(move(leftNode), move(funcRightIn), move(funcReturn), move(rightNode)); } else { return AstExpression::make(move(leftNode), move(centerNode), move(rightNode)); } } else { return AstExpression::make ( i>left ? parseExpression(tokens, left, i-1) : AstVoid::make(), AstToken::make(tokens[i]), i& tokens, int left, int right, vector& nodes) { int start=left; for (int i=left; i<=right; i++) { if (tokens[i]->getOp()==ops->import) { if (i==right || tokens[i+1]->getType()!=TokenData::STRING_LITERAL) { throw PineconeError("'"+ops->import->getText()+"' must be followed by a string literal", SOURCE_ERROR, tokens[i]); } string path=tokens[i+1]->getText(); //this nonesens is required because my lexer is shit and includes the first quote but not the last one //instead of hardcoding that in, I figured I'd make it flexable so I don't break everthing when I fix the lexer while (path.size()>0 && path[0]=='"') path=path.substr(1, string::npos); while (path.size()>0 && path[path.size()-1]=='"') path=path.substr(0, path.size()-1); path=tokens[i]->getFile()->getDirPath()+"/"+path; try { importFile(nodes, path); } catch (PineconeError err) { err.log(); } i+=2; start=i; } else { if (ops->isOpenBrac(tokens[i]->getOp())) { i=skipBrace(tokens, i); } bool tokenTakesRightInput=(tokens[i]->getOp() && tokens[i]->getOp()->takesRightInput()); int next=i+1; bool nextTokenTakesLeftInput=(next<=right && tokens[next]->getOp() && tokens[next]->getOp()->takesLeftInput()); if (i==right || (!tokenTakesRightInput && !nextTokenTakesLeftInput)) { try { AstNode node=parseExpression(tokens, start, i); nodes.push_back(move(node)); } catch (PineconeError err) { err.log(); } start=next; } } } } /*AstNode parseTokenList(const vector& tokens, int left, int right) { vector nodes; while (left<=right) { int i=left; while(true) { auto op=tokens[i]->getOp(); if (op==ops->openPeren || op==ops->openSqBrac || op==ops->openCrBrac) i=skipBrace(tokens, i); if (i>=right) // at the end { if (nodes.empty()) { return parseExpression(tokens, left, right); } else { try { nodes.push_back(parseExpression(tokens, left, right)); } catch (PineconeError err) { err.log(); } break; } } else if (!tokens[i]->getOp() && !tokens[i+1]->getOp())// if the left can't absorb the right and the right cant absorbe the left { nodes.push_back(parseExpression(tokens, left, i)); break; } i++; } left=i+1; } return AstList::make(nodes); }*/ void parseSequence(const vector& tokens, int left, int right, Operator splitter, vector& out) { int start=left; for (int i=left; i<=right; i++) { if (ops->isOpenBrac(tokens[i]->getOp())) { i=skipBrace(tokens, i); } else if (tokens[i]->getOp()==splitter) { if (start<=i-1) out.push_back(parseExpression(tokens, start, i-1)); start=i+1; } else if (tokens[i]->getOp() && tokens[i]->getOp()->getPrecedence()==splitter->getPrecedence()) { break; } else if (tokens[i]->getOp() && tokens[i]->getOp()->getPrecedence()getPrecedence()) { out.clear(); out.push_back(parseExpression(tokens, left, right)); return; } } if (start<=right) out.push_back(parseExpression(tokens, start, right)); } unique_ptr parseType(const vector& tokens, int left, int right) { vector types; while (left<=right) { // skip commas while (left<=right && tokens[left]->getOp()==ops->comma) { left++; } // if this is a named subtype if (left+2<=right && tokens[left+1]->getOp()==ops->colon) { if (tokens[left]->getType()!=TokenData::IDENTIFIER) { throw PineconeError("identifier must be to the left of ':' in type", SOURCE_ERROR, tokens[left]); } Token name=tokens[left]; unique_ptr type; if (tokens[left+2]->getType()==TokenData::IDENTIFIER) { type=AstTokenType::make(tokens[left+2]); left+=3; } else if (tokens[left+2]->getOp()==ops->openCrBrac) { int j=skipBrace(tokens, left+2); if (j>right) { throw PineconeError(FUNC+" skipping brance went outside of range", INTERNAL_ERROR, tokens[left+1]); } type=parseType(tokens, left+2+1, j-1); left=j+1; } else { throw PineconeError("invalid thingy '"+tokens[left+2]->getText()+"' in type", SOURCE_ERROR, tokens[left+2]); } types.push_back(AstTupleType::NamedType{name, move(type)}); } else // this is an unnamed subtype { types.push_back(AstTupleType::NamedType{nullptr, AstTokenType::make(tokens[left])}); left+=1; } } if (types.size()==0) return AstVoidType::make(); else if (types.size()==1 && !types[0].name) return move(types[0].type); else return AstTupleType::make(types); } /* Type parseTypeToken(Token token, Namespace table) { if (token->getType()==TokenData::IDENTIFIER) { Type type=table->getType(token->getText()); if (type) { return type; } else { error.log("could not find type "+token->getDescription(), SOURCE_ERROR, token); return Void; } } else { error.log(FUNC+" called with non identifier token "+token->getText(), INTERNAL_ERROR, token); return Void; } } */ void importFile(vector& nodes, string path) { auto file=shared_ptr(new SourceFile(path, false)); if (file->getContents().empty()) { throw PineconeError("file '"+path+"' failed to open or was empty", SOURCE_ERROR); } vector tokens; lexString(file, tokens); parseTokenList(tokens, 0, tokens.size()-1, nodes); } ================================================ FILE: src/PineconeProgram.cpp ================================================ #include "../h/PineconeProgram.h" #include "../h/ErrorHandler.h" #include "../h/Operator.h" #include "../h/AllOperators.h" #include "../h/CppProgram.h" void populatePineconeStdLib(); void lexString(shared_ptr file, vector& tokens); Action parseFunction(const vector& tokens, int left, int right, Type leftInType, Type rightInType); extern Namespace globalNamespace; PineconeProgram::PineconeProgram() { } void PineconeProgram::cleanUp() { } void PineconeProgram::resolveProgram(string inFilename, bool printOutput) { AllOperators::init(); populatePineconeStdLib(); //initialProgramPopulation(); //globalFrame.resolve(printOutput); if (!error.getIfErrorLogged()) { try { file=shared_ptr(new SourceFile(inFilename, printOutput)); if (printOutput) { cout << endl << endl << file->getBoxedString() << endl; } } catch (PineconeError err) { err.log(); } } if (!error.getIfErrorLogged()) { try { lexString(file, tokens); } catch (PineconeError err) { err.log(); astRoot=AstVoid::make(); } /* if (printOutput) { cout << endl << tableStringFromTokens(tokens, "lexer output") << endl; } */ } //astRoot=parseFunction(tokens, 0, tokens.size()-1, Void, Void); if (!error.getIfErrorLogged()) { try { astRoot=astNodeFromTokens(tokens, 0, tokens.size()-1); } catch (PineconeError err) { err.log(); astRoot=AstVoid::make(); } if (printOutput) { cout << " ╭──────────────────────╮" << endl; cout << " │ abstract syntax tree │" << endl; cout << " ╰──────────────────────╯" << endl; cout << astRoot->getString() << endl; //cout << endl << putStringInBox(astRoot->getString(), "abstract syntax tree") << endl; //cout << endl << str::getBoxedString(astRoot->getString(), "abstract syntax tree") << endl; } } if (!error.getIfErrorLogged()) { try { astRoot->setInput(globalNamespace, true, Void, Void); } catch (PineconeError err) { err.log(); astRoot=AstVoid::make(); } try { actionRoot=astRoot->getAction(); if (printOutput) { cout << " ╭─────────────╮" << endl; cout << " │ action tree │" << endl; cout << " ╰─────────────╯" << endl; cout << actionRoot->getDescription() << endl; //cout << endl << str::getBoxedString(actionRoot->getDescription(), "action tree") << endl; } } catch (PineconeError err) { err.log(); } } /*if (printOutput) { cout << endl << "C source code:\n" << astRoot->getCSource() << endl; }*/ } string PineconeProgram::getCpp() { try { CppProgram outProg; actionRoot->addToProg(voidAction, voidAction, &outProg); return outProg.getCppCode(); } catch (PineconeError err) { err.log(); return "/* no program due to transpiling error */"; } } void PineconeProgram::execute() { try { stackPtr=globalFramePtr=malloc(globalNamespace->getStackFrame()->getSize()); free(actionRoot->execute(nullptr, nullptr)); free(globalFramePtr); stackPtr=globalFramePtr=nullptr; } catch (PineconeError err) { err.log(); cout << endl << ">>>>>> program aborted due to error <<<<<<" << endl; } } ================================================ FILE: src/PineconeStdLib.cpp ================================================ #include "../h/PineconeProgram.h" #include "../h/AllOperators.h" #include "../h/StackFrame.h" #include "../h/Namespace.h" #include "../h/CppProgram.h" #include "../h/utils/stringUtils.h" #include "../h/Type.h" #define CONCAT(a,b) a##_##b #define GET_TYPES_Tuple(t0, t1) t0, t1 #define assert if ( #define otherwise ) {} else #define getPncnType(typeIn) CONCAT(PNCN, typeIn) #define getCppType(typeIn) CONCAT(CPP, typeIn) #define CPP_Dub double #define CPP_Byte unsigned char #define CPP_Int int #define CPP_Bool bool #define CPP_Void char #define PNCN_String String #define PNCN_Dub Dub #define PNCN_Int Int #define PNCN_Byte Byte #define PNCN_Bool Bool #define PNCN_Void Void //#define PNCN_Tuple(t1, t2) Type(new TypeData(vector({PNCN##_##t1, PNCN##_##t2}), "")) #define PNCN_Tuple(t1, t2) makeTuple(vector({{"a", t1}, {"b", t2}}), true) #define LAMBDA_HEADER [](void* leftIn, void* rightIn)->void* #define ADD_CPP_HEADER [](Action left, Action right, CppProgram* prog)->void #define retrn out= #define GET_PTR_VAL(typeIn, varInName) =*((getCppType(typeIn)*)(varInName)) #define DO_INSTANTIATE(typeIn, varOutName, valIn) getCppType(typeIn) varOutName valIn; #define DONT_INSTANTIATE(typeIn, varOutName, valIn) ; #define INSTANTIATE_CPP_TUPLE(t0, t1, varOutName, valIn)\ DO_INSTANTIATE(t0, CONCAT(varOutName, 0), valIn)\ DO_INSTANTIATE(t1, CONCAT(varOutName, 1), ((char *)valIn)+sizeof(getCppType(t0)))\ #define DO_RETURN_VAL(typeIn, varName) void* outPtr=malloc(getPncnType(typeIn)->getSize()); memcpy(outPtr, &varName, getPncnType(typeIn)->getSize()); return outPtr; #define DONT_RETURN_VAL(typeIn, varName) return nullptr; #define INSTANTIATE_String DO_INSTANTIATE #define INSTANTIATE_Dub DO_INSTANTIATE #define INSTANTIATE_Int DO_INSTANTIATE #define INSTANTIATE_Byte DO_INSTANTIATE #define INSTANTIATE_Bool DO_INSTANTIATE #define INSTANTIATE_Void DONT_INSTANTIATE #define INSTANTIATE_Tuple__(typeIn, varOutName, valIn) INSTANTIATE_CPP_TUPLE(typeIn, varOutName, valIn) #define INSTANTIATE_Tuple_(typeIn, varOutName, valIn) INSTANTIATE_Tuple__(GET_TYPES##_##typeIn, varOutName, valIn) #define INSTANTIATE_Tuple(t1, t2) INSTANTIATE_Tuple_ #define RETURN_Dub DO_RETURN_VAL #define RETURN_Int DO_RETURN_VAL #define RETURN_Byte DO_RETURN_VAL #define RETURN_Bool DO_RETURN_VAL #define RETURN_Void DONT_RETURN_VAL #define func(nameText, leftType, rightType, returnType, lambdaBody, cpp) \ addAction(nameText, getPncnType(leftType), getPncnType(rightType), getPncnType(returnType), LAMBDA_HEADER\ { \ INSTANTIATE##_##leftType(leftType, left, GET_PTR_VAL(leftType, leftIn)) \ INSTANTIATE##_##rightType(rightType, right, GET_PTR_VAL(rightType, rightIn)) \ INSTANTIATE##_##returnType(returnType, out, ;) \ lambdaBody; \ CONCAT(RETURN, returnType)(returnType, out) \ }, \ cpp \ ) \ Action voidAction; //shared_ptr stdLibStackFrame; Namespace globalNamespace; Namespace table; // is set to equal globalNamespace, not sure why I have both. I think just because table is shorter (so its used in this file) and globalNamespace is clearer (so its used everywhere else) string getText(Operator op) {return op->getText();} string getText(string in) {return in;} void addConst(void* data, Type type, string name) { globalNamespace->addNode(AstActionWrapper::make(constGetAction(data, type, name, globalNamespace)), name); } template void addAction(T id, Type leftType, Type rightType, Type returnType, function lambda, function addCppToProg) { globalNamespace->addNode(AstActionWrapper::make(lambdaAction(leftType, rightType, returnType, lambda, addCppToProg, getText(id))), getText(id)); } void addType(Type type, string id) { auto node=AstTypeType::make(type); node->setInput(globalNamespace, false, Void, Void); globalNamespace->addNode(move(node), id); } function stringToLambda(string cppCode) { if (cppCode.empty()) { return nullptr; } return [=](Action inLeft, Action inRight, CppProgram* prog) { int start=0; int i; do { i=searchInString(cppCode, "$", start); if (i<0) { prog->code(cppCode.substr(start, cppCode.size()-start)); } else { prog->code(cppCode.substr(start, i-start)); if (substringMatches(cppCode, i, "$.")) { prog->pushExpr(); inLeft->addToProg(prog); start=i+string("$.").size(); prog->popExpr(); } else if (substringMatches(cppCode, i, "$:")) { prog->pushExpr(); inRight->addToProg(prog); start=i+string("$:").size(); prog->popExpr(); } else if (substringMatches(cppCode, i, "$$")) { prog->code("$"); start=i+string("$$").size(); } else { throw PineconeError("invalid '$' escape in C++ code: "+cppCode, INTERNAL_ERROR); } } } while (i>=0); //if (prog->getExprLevel()==0) // prog->endln(); }; } void addAction(string text, Type leftType, Type rightType, Type returnType, function lambda, string cpp) { addAction(text, leftType, rightType, returnType, lambda, stringToLambda(cpp)); } void addAction(Operator op, Type leftType, Type rightType, Type returnType, function lambda, string cpp) { addAction(op, leftType, rightType, returnType, lambda, stringToLambda(cpp)); } Type IntArray=nullptr; Type Array=nullptr; template inline T getValFromTuple(void* data, Type type, string name) { while (type->getType()==TypeBase::PTR) { type=type->getSubType(); data=*(void**)data; } OffsetAndType a=type->getSubType(name); if (!a.type) throw PineconeError("tried to get invalid property '"+name+"' from type "+type->getString(), INTERNAL_ERROR); return *((T*)((char*)data+a.offset)); } /* template inline T getValFromTuple(void* data, Type type, int index) { if (index<0 || index>=type->getAllSubTypes()->size()) throw PineconeError("tried to get invalid property #"+to_string(index)+" from tuple "+type->getString(), INTERNAL_ERROR); int offset=0; for (int i=0; igetAllSubTypes())[i]->getSize(); } return *((T*)((char*)data+offset)); } */ template inline void setValInTuple(void* data, Type type, string name, T val) { while (type->getType()==TypeBase::PTR) { type=type->getSubType(); data=*(void**)data; } OffsetAndType a=type->getSubType(name); if (!a.type) throw PineconeError("tried to set invalid property '"+name+"' from type "+type->getString(), INTERNAL_ERROR); *((T*)((char*)data+a.offset))=val; } inline string pncnStr2CppStr(void* obj) { int len=getValFromTuple(obj, String, "_size"); char * data=(char*)malloc((len+1)*sizeof(char)); memcpy(data, getValFromTuple(obj, String, "_data"), len*sizeof(char)); data[len]=0; string out(data); return out; } inline void* cppStr2PncnStr(string cpp) { void * obj=malloc(String->getSize()); char * strData=(char*)malloc(cpp.size()*sizeof(char)); memcpy(strData, cpp.c_str(), cpp.size()*sizeof(char)); *((int*)((char*)obj+String->getSubType("_size").offset))=cpp.size(); *((char**)((char*)obj+String->getSubType("_data").offset))=strData; return obj; } /// add C++ funcs to program void addToProgPnStr(CppProgram * prog) { if (!prog->hasFunc("$pnStr")) { string strType=prog->getTypeCode(String); prog->addFunc("$pnStr", {{"const char *", "in"}}, strType, "int size = 0;\n" "while (in[size]) size++;\n" //"unsigned char * data = (unsigned char *)malloc(size);\n" //"memcpy(data, in, s ize);\n" //"return "+strType+"(size, data);\n" "return "+strType+"(size, (unsigned char*)in);\n" ); } } void addToProgCStr(CppProgram * prog) { if (!prog->hasFunc("$cStr")) { if (prog->hasFunc("$cStr")) return; string strType=prog->getTypeCode(String); prog->addFunc("$cStr", {{strType, "in"}}, "char *", "char * out = (char *)malloc(in._size+1);\n" "memcpy(out, in._data, in._size);\n" "out[in._size] = 0;\n" "return out;\n" ); } } void addToProgSubStr(CppProgram * prog) { if (!prog->hasFunc("$subStr")) { string strType=prog->getTypeCode(String); prog->addFunc("$subStr", {{strType, "in"}, {"int", "a"}, {"int", "b"}}, strType, "int size = b - a;\n" "unsigned char * data = (unsigned char *)malloc(size);\n" "memcpy(data, in._data + a, size);\n" "return "+strType+"(size, data);\n" ); } } void addToProgIntToStr(CppProgram * prog) { if (!prog->hasFunc("$intToStr")) { string strType=prog->getTypeCode(String); prog->addFunc("$intToStr", {{"int", "in"}}, prog->getTypeCode(String), "bool neg = in < 0;\n" "if (neg) in *= -1;\n" "int val = in;\n" "int size = 0;\n" "while (val)\n" "{\n" " size++;\n" " val /= 10;\n" "}\n" "if (size == 0)\n\tsize = 1;\n" "if (neg)\n\tsize++;\n" "unsigned char * data = (unsigned char *)malloc(size);\n" "val = in;\n" "for (int i = 0; i<(neg ? size-1 : size); i++)\n" "{\n" " data[size-i-1] = (val % 10) + '0';\n" " val /= 10;\n" "}\n" "if (neg)\n\tdata[0] = '-';\n" "return "+strType+"(size, data);\n" ); } } void addToProgStrToInt(CppProgram * prog) { if (!prog->hasFunc("$strToInt")) { string strType=prog->getTypeCode(String); prog->addFunc("$strToInt", {{strType, "in"}}, "int", "int out = 0;\n" "for (int i = 0; i < in._size; i++)\n" "{\n" " if (in._data[i] >= '0' && in._data[i] <= '9')\n" " {\n" " out = out * 10 + in._data[i] - '0';\n" " }\n" " else if (in._data[i] == '.')\n" " break;\n" "}\n" "if (in._size > 0 && in._data[0] == '-')\n" " out *= -1;\n" "return out;\n" ); } } void addToProgStrToDub(CppProgram * prog) { if (!prog->hasFunc("$strToDub")) { string strType=prog->getTypeCode(String); prog->addFunc("$strToDub", {{strType, "in"}}, "double", "double out = 0;\n" "int divider = 1;\n" "for (int i = 0; i < in._size; i++)\n" "{\n" " if (divider == 1)\n" " {\n" " if (in._data[i] >= '0' && in._data[i] <= '9')\n" " out = out * 10 + in._data[i] - '0';\n" " else if (in._data[i] == '.')\n" " divider = 10;\n" " }\n" " else\n" " {\n" " if (in._data[i] >= '0' && in._data[i] <= '9')\n" " {\n" " out += ((double)(in._data[i] - '0')) / (double)divider;\n" " divider *= 10;\n" " }\n" " }\n" "}\n" "if (in._size > 0 && in._data[0] == '-')\n" " out *= -1;\n" "return out;\n" ); } } void addToProgConcatStr(CppProgram * prog) { if (!prog->hasFunc("$concatStr")) { string strType=prog->getTypeCode(String); prog->addFunc("$concatStr", {{strType, "a"}, {strType, "b"}}, strType, "int newSize = a._size + b._size;\n" "unsigned char * newData = (unsigned char *)malloc(newSize);\n" "memcpy(newData, a._data, a._size);\n" "memcpy(newData + a._size, b._data, b._size);\n" "return "+strType+"(newSize, newData);\n" ); } } void addToProgDoubleToStr(CppProgram * prog) { if (!prog->hasFunc("$doubleToStr")) { addToProgIntToStr(prog); addToProgConcatStr(prog); addToProgPnStr(prog); string intToStr=prog->pnToCpp("$intToStr"); string concat=prog->pnToCpp("$concatStr"); string pnStr=prog->pnToCpp("$pnStr"); prog->addFunc("$doubleToStr", {{"double", "in"}}, prog->getTypeCode(String), "long long b = (in - (long long)in) * 10000000000 * (in>=0 ? 1 : -1);\n" "if (b % 10 == 9)\n\tb += 1;\n" "while (b>0 && !(b % 10))\n\tb /= 10;\n" "return "+concat+"("+concat+"("+intToStr+"((int)in), "+pnStr+"(\".\")), "+intToStr+"(b));\n" ); } } void addToProgAsciiToStr(CppProgram * prog) { if (!prog->hasFunc("$asciiToStr")) { string strType=prog->getTypeCode(String); prog->addFunc("$asciiToStr", {{"int", "in"}}, strType, "unsigned char * data = (unsigned char *)malloc(1);\n" "*data = in;\n" "return "+strType+"(1, data);\n" ); } } void addToProgGetInputLine(CppProgram * prog) { if (!prog->hasFunc("$getInputLine")) { string strType=prog->getTypeCode(String); addToProgCStr(prog); prog->addFunc("$getInputLine", {{strType, "prompt"}}, strType, "fputs("+prog->pnToCpp("$cStr")+"(prompt), stdout);\n" "fflush(stdout);\n" "size_t bufferSize = 4096;\n" "char buffer[bufferSize];\n" "if (fgets(buffer, bufferSize, stdin))\n" "{\n" " int size = strlen(buffer);\n" " while (size > 0 && buffer[size - 1] == '\\n')\n" " size-=1;\n" " unsigned char * data = (unsigned char *)malloc(size);\n" " memcpy(data, buffer, size);\n" " return "+strType+"(size, data);\n" "}\nelse\n{\n" " return "+strType+"(0, NULL);\n" "}" ); } } void addToProgEqStr(CppProgram * prog) { if (!prog->hasFunc("$eqStr")) { string strType=prog->getTypeCode(String); prog->addFunc("$eqStr", {{strType, "a"}, {strType, "b"}}, "bool", "if (a._size != b._size)\n" " return false;\n" "for (int i = 0; i < a._size; i++)\n" "{\n" " if (a._data[i] != b._data[i])\n" " return false;\n" "}\n" "return true;\n" ); } } void addToProgRunCmd(CppProgram * prog) { if (!prog->hasFunc("$runCmd")) { addToProgPnStr(prog); addToProgCStr(prog); addToProgConcatStr(prog); addToProgAsciiToStr(prog); string strType=prog->getTypeCode(String); prog->addFunc("$runCmd", {{strType, "cmd"}}, strType, strType+" result = "+prog->pnToCpp("$pnStr")+"(\"\");\n" "FILE* pipe = popen(cStr(cmd), \"r\");\n" "if (!pipe)\n" " return result;\n" "while (!feof(pipe)) {\n" " char c;\n" " if ((c=getc(pipe)) != EOF)\n" " {\n" " result="+prog->pnToCpp("$concatStr")+"(result, "+prog->pnToCpp("$asciiToStr")+"(c));\n" " }\n" "}\n" "pclose(pipe);\n" "return result;\n" ); } } void addToProgMakeIntArray(CppProgram * prog) { if (!prog->hasFunc("$makeIntArray")) { string aryType=prog->getTypeCode(IntArray); prog->addFunc("$makeIntArray", {{"int", "size"}}, aryType, "int * data = (int *)malloc(size * sizeof(int));\n" "return "+aryType+"(size, data);\n" ); } } void addToProgStrWithEscapedNames(CppProgram * prog, string str) { int i=0; string buffer = ""; while (i < (int)str.size()) { if (str[i] == '$') { i++; prog->code(buffer); buffer = ""; while (i < (int)str.size()) { char c = str[i]; if (!( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') )) break; buffer += c; i++; } if (buffer.empty()) prog->code("$"); else prog->name(buffer); buffer = ""; } else { buffer += str[i]; i++; } } prog->code(buffer); } /// setup Pinecone std lib void basicSetup() { table=globalNamespace=NamespaceData::makeRootNamespace(); //this makes a new void action after type constants have been created, if left to the original the Void type may not be set up yet voidAction=createNewVoidAction(); } void populateBasicTypes() { String=makeTuple(vector{NamedType{"_size", Int}, NamedType{"_data", Byte->getPtr()}}, false); addType(Void, "Void"); addType(Bool, "Bool"); addType(Byte, "Byte"); addType(Int, "Int"); addType(Dub, "Dub"); addType(String, "String"); addType(Whatev, "Whatev"); } void populateConstants() { table->addNode(AstActionWrapper::make(voidAction), "void"); bool trueVal=true; addConst(&trueVal, Bool, "tru"); bool falseVal=false; addConst(&falseVal, Bool, "fls"); // version constant { Type versionTupleType= makeTuple(vector{ NamedType{"x", Int}, NamedType{"y", Int}, NamedType{"z", Int}, }, false); void* versionTupleData=malloc(versionTupleType->getSize()); setValInTuple(versionTupleData, versionTupleType, "x", VERSION_X); setValInTuple(versionTupleData, versionTupleType, "y", VERSION_Y); setValInTuple(versionTupleData, versionTupleType, "z", VERSION_Z); addConst(versionTupleData, versionTupleType, "VERSION"); } // OS bool isLinux=false; bool isWindows=false; bool isUnix=false; bool isMac=false; #ifdef __linux__ isLinux=true; isUnix=true; #else #ifdef _WIN32 //works forboth 32 and 64 bit systems isWindows=true; #else #ifdef __APPLE__ isMac=true; isUnix=true; #endif // __APPLE__ #endif // _WIN32 #endif // __linux__ addConst(&isLinux, Bool, "OS_IS_LINUX"); addConst(&isWindows, Bool, "OS_IS_WINDOWS"); addConst(&isMac, Bool, "OS_IS_MAC"); addConst(&isUnix, Bool, "OS_IS_UNIX"); func("IS_TRANSPILED", Void, Void, Bool, retrn false; , "true" ); addAction("arg", Void, Int, String, LAMBDA_HEADER { int right = *(int*)rightIn; if (right < (int)cmdLineArgs.size()) { return cppStr2PncnStr(cmdLineArgs[right]); } else { return cppStr2PncnStr(""); } }, ADD_CPP_HEADER { addToProgPnStr(prog); prog->pushExpr(); prog->pushExpr(); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); prog->code(" < argc"); prog->popExpr(); prog->code("?"); prog->pushExpr(); prog->name("$pnStr"); prog->pushExpr(); prog->code("argv["); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); prog->code("]"); prog->popExpr(); prog->popExpr(); prog->code(":"); prog->pushExpr(); prog->name("$pnStr"); prog->pushExpr(); prog->code("\"\""); prog->popExpr(); prog->popExpr(); prog->popExpr(); } ); func("argLen", Void, Void, Int, retrn cmdLineArgs.size(); , "argc" ); } void populateOperators() { /// + func(ops->plus, Int, Int, Int, retrn left+right; , "$. + $:" ); func(ops->plus, Dub, Dub, Dub, retrn left+right; , "$. + $:" ); /// - func(ops->minus, Int, Int, Int, retrn left-right; , "$. - $:" ); func(ops->minus, Dub, Dub, Dub, retrn left-right; , "$. - $:" ); func(ops->minus, Void, Int, Int, retrn -right; , "- $:" ); func(ops->minus, Void, Dub, Dub, retrn -right; , "- $:" ); /// * func(ops->multiply, Int, Int, Int, retrn left*right; , "$. * $:" ); func(ops->multiply, Dub, Dub, Dub, retrn left*right; , "$. * $:" ); /// / func(ops->divide, Int, Int, Int, retrn left/right; , "$. / $:" ); func(ops->divide, Dub, Dub, Dub, retrn left/right; , "$. / $:" ); /// % func(ops->mod, Int, Int, Int, retrn left%right; , "$. % $:" ); func(ops->mod, Dub, Dub, Dub, retrn left-int(left/right)*right; , "$. - int($. / $:) * $:" ); /// = func(ops->equal, Bool, Bool, Bool, retrn left==right; , "$. == $:" ); func(ops->equal, Int, Int, Bool, retrn left==right; , "$. == $:" ); func(ops->equal, Dub, Dub, Bool, retrn left==right; , "$. == $:" ); /// > func(ops->greater, Int, Int, Bool, retrn left>right; , "$. > $:" ); func(ops->greater, Dub, Dub, Bool, retrn left>right; , "$. > $:" ); // < func(ops->less, Int, Int, Bool, retrn leftless, Dub, Dub, Bool, retrn left= func(ops->greaterEq, Int, Int, Bool, retrn left>=right; , "$. >= $:" ); func(ops->greaterEq, Dub, Dub, Bool, retrn left>=right; , "$. >= $:" ); // <= func(ops->lessEq, Int, Int, Bool, retrn left<=right; , "$. <= $:" ); func(ops->lessEq, Dub, Dub, Bool, retrn left<=right; , "$. <= $:" ); // ! func(ops->notOp, Void, Bool, Bool, retrn !right; , "!$:" ); func(ops->notOp, Void, Int, Bool, retrn right==0; , "$: == 0" ); func(ops->notOp, Void, Dub, Bool, retrn right==0; , "$: == 0" ); } void populateConverters() { ///initalizers func("Bool", Void, Void, Bool, retrn false; , "false" ); func("Int", Void, Void, Int, retrn 0; , "0" ); func("Dub", Void, Void, Dub, retrn 0.0; , "0.0" ); func("Byte", Void, Void, Byte, retrn 0; , "0" ); ///casting //to bool func("Bool", Void, Bool, Bool, retrn right , "$:" ); func("Bool", Void, Int, Bool, retrn right!=0 , "($: != 0)" ); func("Bool", Void, Dub, Bool, retrn right!=0 , "($: != 0.0)" ); func("Bool", Bool, Void, Bool, retrn left , "$." ); func("Bool", Int, Void, Bool, retrn left!=0 , "($. != 0)" ); func("Bool", Dub, Void, Bool, retrn left!=0 , "($. != 0.0)" ); //to Byte func("Byte", Void, Bool, Byte, retrn (right?1:0) , "($: ? 1 : 0)" ); func("Byte", Void, Int, Byte, retrn (unsigned char)right , "((unsigned char)$:)" ); func("Byte", Bool, Void, Byte, retrn (left?1:0) , "($. ? 1 : 0)" ); func("Byte", Int, Void, Byte, retrn (unsigned char)left , "((unsigned char)$.)" ); //to Int func("Int", Void, Bool, Int, retrn (right?1:0) , "($: ? 1 : 0)" ); func("Int", Void, Byte, Int, retrn (int)right , "((int)$:)" ); func("Int", Void, Dub, Int, retrn (int)right , "((int)$:)" ); func("Int", Bool, Void, Int, retrn (left?1:0) , "($. ? 1 : 0)" ); func("Int", Byte, Void, Int, retrn (int)left , "((int)$.)" ); func("Int", Dub, Void, Int, retrn (int)left , "((int)$.)" ); addAction("Int", String, Void, Int, LAMBDA_HEADER { int* out=(int*)malloc(sizeof(int)); *out=stringToInt(pncnStr2CppStr(leftIn)); return out; }, ADD_CPP_HEADER { addToProgStrToInt(prog); prog->name("$strToInt"); prog->pushExpr(); left->addToProg(prog); prog->popExpr(); } ); //to Dub func("Dub", Void, Bool, Dub, retrn (right?1:0) , "($: ? 1.0 : 0.0)" ); func("Dub", Void, Int, Dub, retrn (double)right , "((double)$:)" ); func("Dub", Bool, Void, Dub, retrn (left?1:0) , "($. ? 1.0 : 0.0)" ); func("Dub", Int, Void, Dub, retrn (double)left , "((double)$.)" ); addAction("Dub", String, Void, Dub, LAMBDA_HEADER { double* out=(double*)malloc(sizeof(double)); *out=stringToDouble(pncnStr2CppStr(leftIn)); return out; }, ADD_CPP_HEADER { addToProgStrToDub(prog); prog->name("$strToDub"); prog->pushExpr(); left->addToProg(prog); prog->popExpr(); } ); } void populateStdFuncs() { //print func("print", Void, Void, Void, cout << endl; , "fputs(\"\\n\", stdout)" ); func("print", Void, Bool, Void, cout << (right?"tru":"fls") << endl; , "fputs($:?\"tru\\n\":\"fls\\n\", stdout)" ); func("print", Void, Byte, Void, cout << right << endl; , "printf(\"%c\", $:)" ); func("print", Void, Int, Void, cout << right << endl; , "printf(\"%d\\n\", $:)" ); func("print", Void, Dub, Void, cout << doubleToString(right) << endl; , ADD_CPP_HEADER { addToProgDoubleToStr(prog); addToProgCStr(prog); prog->code("printf"); prog->pushExpr(); prog->code("\"%s\\n\", "); prog->name("$cStr"); prog->pushExpr(); prog->name("$doubleToStr"); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); prog->popExpr(); prog->popExpr(); } ); addAction("print", Void, String, Void, LAMBDA_HEADER { cout << pncnStr2CppStr(rightIn) << endl; return nullptr; }, ADD_CPP_HEADER { addToProgCStr(prog); prog->code("printf"); prog->pushExpr(); prog->code("\"%s\\n\", "); prog->name("$cStr"); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); prog->popExpr(); } ); } void populateTypeInfoFuncs() { globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { if (leftType->isVoid() || !rightType->isVoid()) return nullptr; string val=leftType->getName(); return lambdaAction( leftType, rightType, String, [=](void* leftIn, void* rightIn) -> void* { return cppStr2PncnStr(val); }, [=](Action inLeft, Action inRight, CppProgram* prog) { void* pnStr=cppStr2PncnStr(val); constGetAction(pnStr, String, val, globalNamespace)->addToProg(prog); free(pnStr); }, "typeName" ); } ), "typeName" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { if (leftType->isVoid() || !rightType->isVoid()) return nullptr; int val=leftType->getSize(); return lambdaAction( leftType, rightType, Int, [=](void* leftIn, void* rightIn) -> void* { return &(*(int*)malloc(sizeof(int))=val); }, [=](Action inLeft, Action inRight, CppProgram* prog) { constGetAction(&val, Int, to_string(val), globalNamespace)->addToProg(prog); }, "typeSize" ); } ), "typeSize" ); } void populateMemManagementFuncs() { globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { if (!leftType->isVoid() || rightType->isVoid()) return nullptr; return lambdaAction( leftType, rightType, rightType->getPtr(), [=](void* leftIn, void* rightIn) -> void* { size_t size=rightType->getSize(); void ** dataPtr=(void**)malloc(sizeof(void*)); *dataPtr=malloc(size); memcpy(*dataPtr, rightIn, size); return dataPtr; }, [=](Action inLeft, Action inRight, CppProgram* prog) { prog->code("&"); prog->pushExpr(); string typeCode=prog->getTypeCode(rightType); prog->code("(*(" + typeCode + "*)malloc(sizeof(" + typeCode + ")))"); prog->code(" = "); prog->pushExpr(); inRight->addToProg(prog); prog->popExpr(); prog->popExpr(); //"&((*($type)malloc(sizeof($type)))=($data))" }, "new" ); } ), "new" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { if (leftType->getType()!=TypeBase::PTR || leftType->isVoid() || !rightType->isVoid()) return nullptr; return lambdaAction( leftType, rightType, leftType->getSubType(), [=](void* leftIn, void* rightIn) -> void* { size_t size=leftType->getSubType()->getSize(); void * data=malloc(size); memcpy(data, *(void**)leftIn, size); return data; }, [=](Action inLeft, Action inRight, CppProgram* prog) { prog->code("*"); prog->pushExpr(); inLeft->addToProg(prog); prog->popExpr(); }, "dif" ); } ), "dif" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { if (leftType->getType()!=TypeBase::PTR || !rightType->matches(leftType->getSubType())) return nullptr; return lambdaAction( leftType, rightType, Void, [=](void* leftIn, void* rightIn) -> void* { memcpy(*(void**)leftIn, rightIn, rightType->getSize()); return nullptr; }, [=](Action inLeft, Action inRight, CppProgram* prog) { prog->code("*"); prog->pushExpr(); inLeft->addToProg(prog); prog->popExpr(); prog->code(" = "); prog->pushExpr(); inRight->addToProg(prog); prog->popExpr(); }, "dif" ); } ), "dif" ); } void populateStringFuncs() { auto destructorLambda=LAMBDA_HEADER { //if (getValFromTuple(rightIn, String, "_data")[0]==1) // error.log("double free detected", JSYK); //getValFromTuple(rightIn, String, "_data")[0]=1; free(getValFromTuple(rightIn, String, "_data")); return nullptr; }; addAction("__destroy__", Void, String, Void, destructorLambda, ADD_CPP_HEADER { prog->code("free"); prog->pushExpr(); right->addToProg(prog); prog->code("._data"); prog->popExpr(); } ); addAction("__copy__", Void, String, String, LAMBDA_HEADER { int size=getValFromTuple(rightIn, String, "_size"); void* newData=malloc(size); memcpy(newData, getValFromTuple(rightIn, String, "_data"), size); setValInTuple(rightIn, String, "_data", newData); void* out=malloc(String->getSize()); memcpy(out, rightIn, String->getSize()); return out; }, ADD_CPP_HEADER { if (!prog->hasFunc("$copyStr")) { string strType=prog->getTypeCode(String); prog->addFunc("$copyStr", {{strType, "in"}}, strType, "unsigned char* newData=(unsigned char*)malloc(in._size);\n" "memcpy(newData, in._data, in._size);\n" "in._data=newData;\n" "return in;\n" ); } prog->name("$copyStr"); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); } ); addAction("String", Void, Void, String, LAMBDA_HEADER { return cppStr2PncnStr(""); }, ADD_CPP_HEADER { prog->code(prog->getTypeCode(String)+"(0, nullptr)"); } ); addAction("String", String, Void, String, LAMBDA_HEADER { return cppStr2PncnStr(pncnStr2CppStr(leftIn)); }, ADD_CPP_HEADER { left->addToProg(prog); } ); addAction("String", Int, Void, String, LAMBDA_HEADER { return cppStr2PncnStr(to_string(*((int*)leftIn))); }, ADD_CPP_HEADER { addToProgIntToStr(prog); prog->name("$intToStr"); prog->pushExpr(); left->addToProg(prog); prog->popExpr(); } ); addAction("String", Dub, Void, String, LAMBDA_HEADER { return cppStr2PncnStr(doubleToString(*((double*)leftIn))); }, ADD_CPP_HEADER { addToProgDoubleToStr(prog); prog->name("$doubleToStr"); prog->pushExpr(); left->addToProg(prog); prog->popExpr(); } ); addAction("String", Bool, Void, String, LAMBDA_HEADER { if (*((bool*)leftIn)) { return cppStr2PncnStr("tru"); } else { return cppStr2PncnStr("fls"); } }, ADD_CPP_HEADER { addToProgPnStr(prog); prog->name("$pnStr"); prog->pushExpr(); prog->pushExpr(); left->addToProg(prog); prog->popExpr(); prog->code(" ? \"tru\" : \"fls\""); prog->popExpr(); } ); addAction("len", String, Void, Int, LAMBDA_HEADER { int* out=(int*)malloc(sizeof(int)); *out=getValFromTuple(leftIn, String, "_size"); return out; }, ADD_CPP_HEADER { getElemFromTupleAction(String, "_size")->addToProg(left, voidAction, prog); } ); addAction("ascii", Int, Void, String, LAMBDA_HEADER { int val=*((int*)leftIn); if (val<0 || val>=256) { throw PineconeError("tried to make ascii string out of value "+to_string(val), RUNTIME_ERROR); } string out; out+=(char)val; return cppStr2PncnStr(out); }, ADD_CPP_HEADER { addToProgAsciiToStr(prog); prog->name("$asciiToStr"); prog->pushExpr(); left->addToProg(prog); prog->popExpr(); } ); addAction("at", String, Int, Int, LAMBDA_HEADER { int index=*((int*)rightIn); int * out=(int*)malloc(sizeof(int)); string str=pncnStr2CppStr(leftIn); if (index<0 || index>=int(str.size())) { throw PineconeError("tried to access location "+to_string(index)+" in string "+to_string(str.size())+" long", RUNTIME_ERROR); } *out=str[index]; return out; }, ADD_CPP_HEADER { prog->code("(int)"); getElemFromTupleAction(String, "_data")->addToProg(left, voidAction, prog); prog->code("["); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); prog->code("]"); } ); addAction("sub", String, makeTuple(vector{NamedType{"a", Int}, NamedType{"b", Int}}, true), String, LAMBDA_HEADER { Type RightType=makeTuple(vector{NamedType{"a", Int}, NamedType{"b", Int}}, true); int start=getValFromTuple(rightIn, RightType, "a"); int end=getValFromTuple(rightIn, RightType, "b"); string str=pncnStr2CppStr(leftIn); if (start<0 || end>int(str.size()) || start>end) { throw PineconeError("invalid arguments sent to String.sub", RUNTIME_ERROR); } return cppStr2PncnStr(str.substr(start, end-start)); }, ADD_CPP_HEADER { addToProgSubStr(prog); prog->name("$subStr"); prog->pushExpr(); left->addToProg(prog); prog->code(", "); getElemFromTupleAction(right->getReturnType(), "a")->addToProg(right, voidAction, prog); prog->code(", "); getElemFromTupleAction(right->getReturnType(), "b")->addToProg(right, voidAction, prog); prog->popExpr(); } ); addAction("input", String, Void, String, LAMBDA_HEADER { string in; cout << pncnStr2CppStr(leftIn); std::getline (std::cin, in); return cppStr2PncnStr(in); }, ADD_CPP_HEADER { addToProgGetInputLine(prog); prog->name("$getInputLine"); prog->pushExpr(); left->addToProg(prog); prog->popExpr(); } ); addAction(ops->plus, String, String, String, [=](void* leftIn, void* rightIn)->void* { void* out=cppStr2PncnStr(pncnStr2CppStr(leftIn)+pncnStr2CppStr(rightIn)); //free(destructorLambda(nullptr, leftIn)); //free(destructorLambda(nullptr, rightIn)); return out; }, ADD_CPP_HEADER { addToProgConcatStr(prog); prog->name("$concatStr"); prog->pushExpr(); left->addToProg(prog); prog->code(", "); right->addToProg(prog); prog->popExpr(); } ); addAction(ops->equal, String, String, Bool, LAMBDA_HEADER { bool* out=(bool*)malloc(sizeof(bool)); *out=pncnStr2CppStr(leftIn)==pncnStr2CppStr(rightIn); return out; }, ADD_CPP_HEADER { addToProgEqStr(prog); prog->name("$eqStr"); prog->pushExpr(); left->addToProg(prog); prog->code(", "); right->addToProg(prog); prog->popExpr(); } ); } void populateArrayFuncs() { TupleTypeMaker maker; maker.add("_size", Int); maker.add("_capacity", Int); maker.add("_data", Whatev->getPtr()); Array=maker.get(false); addType(Array, "Array"); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { assert leftType->isCreatable() && rightType->isVoid() otherwise return nullptr; Type contentsType=leftType; Type arrayType=Array->actuallyIs(makeTuple({{"a", Int}, {"b", Int}, {"c", contentsType->getPtr()}}, true))->getPtr(); return lambdaAction( leftType, rightType, arrayType, [=](void* leftIn, void* rightIn) -> void* { void* out=malloc(arrayType->getSize()); setValInTuple(out, arrayType, "_size", 0); setValInTuple(out, arrayType, "_capacity", 0); setValInTuple(out, arrayType, "_data", nullptr); return out; }, [=](Action inLeft, Action inRight, CppProgram* prog) { throw PineconeError("not yet implemented", INTERNAL_ERROR); }, "Array" ); } ), "Array" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { assert leftType->matches(Array) otherwise return nullptr; Type arrayType=Array->actuallyIs(leftType->getPtr()); Type contentsType=arrayType->getSubType("_data").type->getSubType(); assert rightType->matches(contentsType) otherwise return nullptr; return lambdaAction( leftType, rightType, Void, [=](void* leftIn, void* rightIn) -> void* { int size=getValFromTuple(leftIn, arrayType, "_size"); int capacity=getValFromTuple(leftIn, arrayType, "_capacity"); void* data=getValFromTuple(leftIn, arrayType, "_data"); if (size+1>capacity) { if (capacity<1000) capacity=1000; else capacity*=2; setValInTuple(leftIn, arrayType, "_capacity", capacity); void* newData=malloc(capacity*contentsType->getSize()); memcpy(newData, data, size*contentsType->getSize()); free(data); data=newData; setValInTuple(leftIn, arrayType, "_data", data); } setValInTuple(leftIn, arrayType, "_size", size+1); memcpy((char*)data+size*contentsType->getSize(), rightIn, contentsType->getSize()); return nullptr; }, [=](Action inLeft, Action inRight, CppProgram* prog) { throw PineconeError("not yet implemented", INTERNAL_ERROR); }, "append" ); } ), "append" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { assert leftType->matches(Array->getPtr()) && rightType->matches(Int) otherwise return nullptr; Type arrayType=Array->actuallyIs(leftType->getSubType()); Type contentsType=arrayType->getSubType("_data").type->getSubType(); size_t elemSize=contentsType->getSize(); return lambdaAction( leftType, rightType, contentsType, [=](void* leftIn, void* rightIn) -> void* { int size=getValFromTuple(leftIn, arrayType, "_size"); if (*(int*)rightIn<0 || *(int*)rightIn>=size) throw PineconeError("index out of bounds, tried to get element at position "+to_string(*(int*)rightIn)+" in array "+to_string(size)+" long", RUNTIME_ERROR); void* out=malloc(contentsType->getSize()); memcpy(out, getValFromTuple(leftIn, arrayType, "_data")+(*(int*)rightIn)*elemSize, elemSize); return out; }, [=](Action inLeft, Action inRight, CppProgram* prog) { throw PineconeError("not yet implemented", INTERNAL_ERROR); }, "get" ); } ), "get" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { Type arrayType=Array->actuallyIs(leftType->getSubType()); Type contentsType=arrayType->getSubType("_data").type->getSubType(); Type inputType=makeTuple({{"index", Int}, {"value", contentsType}}, true); assert leftType->getSubType()->matches(Array) && rightType->matches(inputType) otherwise return nullptr; size_t elemSize=contentsType->getSize(); return lambdaAction( leftType, rightType, contentsType, [=](void* leftIn, void* rightIn) -> void* { int size=getValFromTuple(leftIn, arrayType, "_size"); int index=getValFromTuple(rightIn, inputType, "index"); if (index<0 || index>=size) throw PineconeError("index out of bounds, tried to set element at position "+to_string(index)+" in array "+to_string(size)+" long", RUNTIME_ERROR); char* data=getValFromTuple(leftIn, arrayType, "_data"); memcpy(data+index*elemSize, (char*)rightIn+inputType->getSubType("value").offset, contentsType->getSize()); return nullptr; }, [=](Action inLeft, Action inRight, CppProgram* prog) { throw PineconeError("not yet implemented", INTERNAL_ERROR); }, "set" ); } ), "set" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { assert leftType->matches(Array) && rightType->isVoid() otherwise return nullptr; Type arrayType=Array->actuallyIs(leftType); Type contentsType=arrayType->getSubType("_data").type->getSubType(); return lambdaAction( leftType, rightType, Int, [=](void* leftIn, void* rightIn) -> void* { int* out=(int*)malloc(sizeof(int)); *out=getValFromTuple(leftIn, arrayType, "_size"); return out; }, [=](Action inLeft, Action inRight, CppProgram* prog) { throw PineconeError("not yet implemented", INTERNAL_ERROR); }, "len" ); } ), "len" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { assert leftType->isVoid() && rightType->matches(Array) otherwise return nullptr; Type arrayType=Array->actuallyIs(rightType); Type contentsType=arrayType->getSubType("_data").type->getSubType(); size_t elemSize=contentsType->getSize(); return lambdaAction( Void, rightType, rightType, [=](void* leftIn, void* rightIn) -> void* { //error.log("Array destroyer called", JSYK); int size=getValFromTuple(rightIn, arrayType, "_size"); void* newData=malloc(elemSize*size); void* oldData=getValFromTuple(rightIn, arrayType, "_data"); memcpy(newData, oldData, elemSize*size); setValInTuple(rightIn, arrayType, "_data", newData); void* out=malloc(arrayType->getSize()); memcpy(out, rightIn, arrayType->getSize()); return out; }, [=](Action inLeft, Action inRight, CppProgram* prog) { throw PineconeError("not yet implemented", INTERNAL_ERROR); }, "__copy__" ); } ), "__copy__" ); globalNamespace->addNode( AstWhatevToActionFactory::make( [](Type leftType, Type rightType) -> Action { assert leftType->isVoid() && rightType->matches(Array) otherwise return nullptr; Type arrayType=Array->actuallyIs(rightType); return lambdaAction( Void, rightType, Void, [=](void* leftIn, void* rightIn) -> void* { //error.log("Array destroyer called", JSYK); free(getValFromTuple(rightIn, arrayType, "_data")); return nullptr; }, [=](Action inLeft, Action inRight, CppProgram* prog) { throw PineconeError("not yet implemented", INTERNAL_ERROR); }, "__destroy__" ); } ), "__destroy__" ); } void populateIntArrayAndFuncs() { TupleTypeMaker maker; maker.add("_size", Int); maker.add("_data", Int->getPtr()); IntArray=maker.get(false); addType(IntArray, "IntArray"); //func("IntArray", IntArray, Void, Int, // retrn Tuple(right, (double)((int*)malloc(Int->getSize()*right))); //); addAction("IntArray", Void, Int, IntArray, LAMBDA_HEADER { char* out=(char*)malloc(IntArray->getSize()); int sizeIn=*(int*)rightIn; int* intPtrData=(int *)malloc(Int->getSize()*sizeIn); setValInTuple(out, IntArray, "_size", sizeIn); setValInTuple(out, IntArray, "_data", intPtrData); return out; }, ADD_CPP_HEADER { addToProgMakeIntArray(prog); prog->name("$makeIntArray"); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); } ); addAction("len", IntArray, Void, Int, LAMBDA_HEADER { int* out=(int*)malloc(sizeof(int)); *out=getValFromTuple(leftIn, IntArray, "_size"); return out; }, ADD_CPP_HEADER { getElemFromTupleAction(IntArray, "_size")->addToProg(left, voidAction, prog); } ); addAction("get", IntArray, Int, Int, LAMBDA_HEADER { int pos=*(int*)rightIn; int size=getValFromTuple(leftIn, IntArray, "_size"); if (pos<0 || pos>=size) throw PineconeError("tried to acces position "+to_string(pos)+" of array "+to_string(size)+" long", RUNTIME_ERROR); int* arrayPtr=getValFromTuple(leftIn, IntArray, "_data"); int val=*(arrayPtr+pos); int* out=(int*)malloc(sizeof(int)); *out=val; return out; }, ADD_CPP_HEADER { getElemFromTupleAction(IntArray, "_data")->addToProg(left, voidAction, prog); prog->code("["); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); prog->code("]"); } ); addAction("set", IntArray, PNCN_Tuple(Int, Int), Void, LAMBDA_HEADER { Type rightType=PNCN_Tuple(Int, Int); int pos=getValFromTuple(rightIn, rightType, "a"); int size=getValFromTuple(leftIn, IntArray, "_size"); if (pos<0 || pos>=size) throw PineconeError("tried to set value at position "+to_string(pos)+" of array "+to_string(size)+" long", RUNTIME_ERROR); int val=getValFromTuple(rightIn, rightType, "b"); int* arrayPtr=getValFromTuple(leftIn, IntArray, "_data"); *(arrayPtr+pos)=val; return nullptr; }, ADD_CPP_HEADER { getElemFromTupleAction(IntArray, "_data")->addToProg(left, voidAction, prog); prog->code("["); prog->pushExpr(); getElemFromTupleAction(right->getReturnType(), "a")->addToProg(right, voidAction, prog); prog->popExpr(); prog->code("]"); prog->code(" = "); getElemFromTupleAction(right->getReturnType(), "b")->addToProg(right, voidAction, prog); } ); } void populateNonStdFuncs() { func("printc", Void, Int, Void, putchar((char)right) , "putchar($:)" ); func("inputc", Void, Void, Int, retrn getchar() , "getchar()" ); func("inputInt", Void, Void, Int, int val; std::cin >> val; retrn val; , ADD_CPP_HEADER { if (!prog->hasFunc("-getIntInput")) { prog->pushFunc("-getIntInput", Void, Void, Int); prog->declareVar("tmp", Int); prog->code("scanf"); prog->pushExpr(); prog->code("\"%d\", &"); prog->name("tmp"); prog->popExpr(); prog->endln(); prog->code("return "); prog->name("tmp"); prog->endln(); prog->popFunc(); } prog->name("-getIntInput"); prog->code("()"); } ); addAction("runCmd", Void, String, String, LAMBDA_HEADER { return cppStr2PncnStr(runCmd(pncnStr2CppStr(rightIn))); }, ADD_CPP_HEADER { addToProgRunCmd(prog); prog->name("$runCmd"); prog->pushExpr(); right->addToProg(prog); prog->popExpr(); } ); } void populateCppInterfaceFuncs() { addAction("cppCode", Void, String, Void, LAMBDA_HEADER { throw PineconeError("you can't run interpreter with code that uses 'cppCode'", SOURCE_ERROR); }, ADD_CPP_HEADER { prog->pushBlock(); addToProgStrWithEscapedNames(prog, pncnStr2CppStr(right->execute(nullptr, nullptr))); prog->popBlock(); } ); addAction("cppHead", Void, String, Void, LAMBDA_HEADER { throw PineconeError("you can't run interpreter with code that uses 'cppHead'", SOURCE_ERROR); }, ADD_CPP_HEADER { prog->addHeadCode(pncnStr2CppStr(right->execute(nullptr, nullptr))); } ); } void populatePineconeStdLib() { basicSetup(); populateBasicTypes(); populateConstants(); populateOperators(); populateConverters(); populateStdFuncs(); populateTypeInfoFuncs(); populateMemManagementFuncs(); populateStringFuncs(); populateIntArrayAndFuncs(); populateArrayFuncs(); populateNonStdFuncs(); populateCppInterfaceFuncs(); } ================================================ FILE: src/ResolveLiteral.cpp ================================================ #include "../h/Action.h" #include "../h/Token.h" #include "../h/ErrorHandler.h" #include "../h/Namespace.h" extern Namespace globalNamespace; Action resolveIntLiteral(Token token, Type type) { string in=token->getText(); int val=0; for (auto i=in.begin(); i!=in.end(); ++i) { if (*i<'0' || *i>'9') { error.log(string() + "bad character '" + *i + "' found in number '" + in + "'", SOURCE_ERROR, token); return nullptr; } val=val*10+(*i-'0'); } if (type==Bool) { bool out=(val!=0); return constGetAction(&out, type, token->getText(), globalNamespace); } else { int out=val; return constGetAction(&out, type, token->getText(), globalNamespace); } } Action resolveDubLiteral(Token token) { string in=token->getText(); double val=0; int pointPos=0; for (auto i=in.begin(); i!=in.end(); ++i) { if (*i=='.' || *i=='_') { if (pointPos==0) { pointPos=10; } else { error.log(string() + "multiple decimal points found in number '" + in + "'", SOURCE_ERROR, token); return voidAction; } } else if (*i>='0' && *i<='9') { if (pointPos) { val+=(double)(*i-'0')/pointPos; pointPos*=10; } else { val=val*10+(*i-'0'); } } else { error.log(string() + "bad character '" + *i + "' found in number '" + in + "'", SOURCE_ERROR, token); return voidAction; } } double out=val; return constGetAction(&out, Dub, token->getText(), globalNamespace); } string pncnStr2CppStr(void* obj); void* cppStr2PncnStr(string cpp); Action resolveStringLiteral(Token token) { string text=token->getText(); //this nonesens is required because my lexer is shit and includes the first quote but not the last one //instead of hardcoding that in, I figured I'd make it flexable so I don't break everthing when I fix the lexer while (text.size()>0 && text[0]=='"') text=text.substr(1, string::npos); while (text.size()>0 && text[text.size()-1]=='"') text=text.substr(0, text.size()-1); void* obj=cppStr2PncnStr(text); return constGetAction(obj, String, "\""+text+"\"", globalNamespace); } Action resolveLiteral(Token token) { if (token->getType()==TokenData::STRING_LITERAL) { return resolveStringLiteral(token); } if (token->getType()!=TokenData::LITERAL) { throw PineconeError(FUNC+" called on token that is not a literal", INTERNAL_ERROR, token); } string in=token->getText(); if (in.empty()) return nullptr; //bool floatingPoint=false; Type type=Unknown; if (in.empty()) { error.log("tried to make literal with empty string", INTERNAL_ERROR, token); } if ((in[0]>='0' && in[0]<='9') || in[0]=='.') { type=Int; for (auto i=in.begin(); i!=in.end(); ++i) { if (*i=='.' || *i=='_') { type=Dub; break; } } if (in.back()=='d' || in.back()=='f') { type=Dub; in.pop_back(); } else if (in.back()=='i') { type=Int; in.pop_back(); } else if (in.back()=='b') { type=Bool; in.pop_back(); } } if (type==Int) { return resolveIntLiteral(token, type); } else if (type==Dub) //floating point { return resolveDubLiteral(token); } else { throw PineconeError("tried to make literal with invalid type of " + type->getString(), INTERNAL_ERROR, token); } } ================================================ FILE: src/SourceFile.cpp ================================================ #include "../h/SourceFile.h" #include "../h/utils/fileUtils.h" #include "../h/msclStringFuncs.h" SourceFile::SourceFile(string filenameIn, bool printOutput) { filename=filenameIn; try { loadFile(filenameIn, contents); contents+="\n"; } catch (string err) { throw PineconeError(err, SOURCE_ERROR); } } string SourceFile::getDirPath() { int i=filename.size(); // this will break on windows while (i>=0 && filename[i]!='/') { i--; } return filename.substr(0, i); } string SourceFile::getBoxedString() { return str::getBoxedString(contents, filename, true); } string SourceFile::getLine(int lineNum) { return getTextOfLine(contents, lineNum); } ================================================ FILE: src/StackFrame.cpp ================================================ #include "../h/StackFrame.h" #include "../h/msclStringFuncs.h" #include "../h/ErrorHandler.h" void* globalFramePtr=nullptr; void* stackPtr=nullptr; void StackFrame::addMember(Type in) { if (!in->isCreatable()) { throw PineconeError("tried to insert uncreatable type "+in->getString()+" into stack frame", INTERNAL_ERROR); } members.push_back(in); frameSize+=in->getSize(); } void StackFrame::setInput(Type left, Type right) { if (inputSet) { error.log("StackFrame::setInput called multiple times", INTERNAL_ERROR); } else { if (left->isCreatable()) { leftInputOffset=frameSize; leftInputIndex=members.size(); addMember(left); } else if (left!=Void) { throw PineconeError("stack frame left input set to "+left->getString()+", which is neither creatable nore a normal void", INTERNAL_ERROR); } if (right->isCreatable()) { rightInputOffset=frameSize; rightInputIndex=members.size(); addMember(right); } else if (right!=Void) { throw PineconeError("stack frame right input set to "+right->getString()+", which is neither creatable nore a normal void", INTERNAL_ERROR); } inputSet=true; } } Type StackFrame::getLeftInType() { if (leftInputIndex>=0) { return members[leftInputIndex]; } else { return Void; } } Type StackFrame::getRightInType() { if (rightInputIndex>=0) { return members[rightInputIndex]; } else { return Void; } } size_t StackFrame::getLeftOffset() { if (leftInputIndex>=0) { return leftInputOffset; } else { error.log("tried to get the left input offset from a stack frame that does not have a left input", INTERNAL_ERROR); return 0; } } size_t StackFrame::getRightOffset() { if (rightInputIndex>=0) { return rightInputOffset; } else { throw PineconeError("tried to get the right input offset from a stack frame that does not have a right input", INTERNAL_ERROR); } } ================================================ FILE: src/Token.cpp ================================================ #include "../h/Token.h" #include "../h/msclStringFuncs.h" #include "../h/utils/stringUtils.h" Token makeToken(string textIn, shared_ptr fileIn, int lineIn, int charPosIn, TokenData::Type tokenTypeIn, Operator opIn) { if (str::hasPrefix(textIn, "\"") && !str::hasSuffix(textIn, "\"")) textIn+="\""; return Token(new TokenData(textIn, fileIn, lineIn, charPosIn, tokenTypeIn, opIn)); } Token makeToken(string textIn) { return Token(new TokenData(textIn, nullptr, 0, 0, TokenData::IDENTIFIER, Operator(nullptr))); } string TokenData::typeToString(TokenData::Type in) { switch (in) { case WHITESPACE: return "whitespace"; case LINE_END: return "newline"; case IDENTIFIER: return "identifier"; case LITERAL: return "literal"; case STRING_LITERAL: return "string literal"; case OPERATOR: return "operator"; case LINE_COMMENT: return "single line comment"; case BLOCK_COMMENT: return "block comment"; case SCOPE: return "scope"; case UNKNOWN: return "UNKNOWN"; default: return "ERROR GETTING TOKEN TYPE"; } } string TokenData::getDescription() { return to_string(getLine()) + ":" + to_string(getCharPos()) + " (" + TokenData::typeToString(tokenType) + " '" + getText() + "')"; } string TokenData::getTypeDescription() { string out; if (tokenType==TokenData::OPERATOR) { if (op) { out+=op->getText()+" "; } else { out+="unknown "; } } out+=TokenData::typeToString(tokenType); return out; } string tableStringFromTokens(const vector& tokens, string tableName) { /*string out="Name\tType\n"; for (unsigned i=0; igetText(), "\n", "\\n") + "\t"; out+=tokens[i]->getTypeDescription(); if (i lines; string abv="", blw=""; string str=""; //const int tabSize=1; const int maxWidth=240; const string seporator=" "; //const string seporator=" | "; for (unsigned i=0; igetText(); if (i>0 && str.size()+seporator.size()+tokens[i]->getText().size()getText().size(); j++) { abv+=" "; blw+=" "; } str+=tokens[i]->getText(); } else { if (i>0) { lines.push_back(abv); lines.push_back(str); lines.push_back(blw); lines.push_back(""); abv=""; blw=""; } for (unsigned j=0; jgetText().size(); j++) { abv+=" "; blw+=" "; } str=tokens[i]->getText(); } } lines.push_back(abv); lines.push_back(str); lines.push_back(blw); return lineListToBoxedString(lines, tableName, -1, false, maxWidth+4); } string stringFromTokens(const vector& tokens, int left, int right) { string out; for (int i=left; i<=right; ++i) { out+=tokens[i]->getText(); if (i using std::unique_ptr; using std::get; class VoidType: public TypeBase { public: virtual string getString() { return "VOID"; } string getCompactString() { return "v"; } string getCppLiteral(void * data, CppProgram * prog) { throw PineconeError("tried to get the literal value of 'void;", INTERNAL_ERROR); } bool isCreatable() { return false; } bool isVoid() { return true; } size_t getSize() { return 0; } PrimitiveType getType() { return VOID; }; protected: bool matchesSameTypeType(Type other) { return true; } }; class UnknownType: public TypeBase { public: virtual string getString() { return "UNKNOWN"; } string getCompactString() { return "u"; } string getCppLiteral(void * data, CppProgram * prog) { return "/* can not instantiate unknown type */"; } bool isCreatable() { return false; } bool isVoid() { return false; } size_t getSize() { return 0; } PrimitiveType getType() { return UNKNOWN; }; protected: bool matchesSameTypeType(Type other) { return false; } }; class PrimType: public TypeBase { public: PrimType(PrimitiveType in) { primType=in; } string getCompactString() { switch (primType) { case BOOL: return "b"; case BYTE: return "y"; case INT: return "i"; case DUB: return "d"; default: throw PineconeError("tried to make " + getString() + " compact", INTERNAL_ERROR); } } string getString() { return TypeBase::getString(primType); } string getCppLiteral(void * data, CppProgram * prog) { string val; switch (primType) { case BOOL: val=(*(bool*)data)?"true":"false"; break; case BYTE: val=to_string(*(unsigned char*)data); break; case INT: val=to_string(*(int*)data); break; case DUB: val=doubleToString(*(double*)data); break; /*{ std::ostringstream ss; ss << *(double*)data; val=ss.str(); } break;*/ default: throw PineconeError("tried to convert " + getString() + " to C++ code", INTERNAL_ERROR); } return val; } size_t getSize() { switch (primType) { case BOOL: return sizeof(bool); case BYTE: return sizeof(unsigned char); case INT: return sizeof(int); case DUB: return sizeof(double); default: throw PineconeError("tried to get size of " + getString(), INTERNAL_ERROR); } } PrimitiveType getType() { return primType; } protected: PrimitiveType primType; bool matchesSameTypeType(Type other) { return other->getType()==primType; } }; class TupleType: public TypeBase { public: TupleType(unique_ptr> in, bool isAnonymousIn) { if (in==nullptr) error.log(FUNC+" sent null input, compiler will likely shit itself in the near future", INTERNAL_ERROR); subTypes=std::move(in); for (auto i: *subTypes) { if (i.type->isWhatev()) { hasWhatev=true; } if (!i.type->isCreatable()) { hasOnlyCreatable=false; } } isAnonymous=isAnonymousIn; } ~TupleType() { } string getString() { string out; out+="{"; for (int i=0; isize()); i++) { if (i) out+=", "; out+=(*subTypes)[i].name+": "+(*subTypes)[i].type->getString(); } out+="} (tuple)"; return out; } string getCompactString() { string out; out+="Tt_"; for (int i=0; isize()); i++) { //out+=(*subTypes)[i].name.size()+"_"+(*subTypes)[i].name+"_"+(*subTypes)[i].type->getCompactString()+"_"; out+=(*subTypes)[i].type->getCompactString()+"_"; } out+="tT"; return out; } string getCppLiteral(void * data, CppProgram * prog) { if (subTypes->size()==1) { return (*subTypes)[0].type->getCppLiteral(data, prog); } string out; out+=prog->getTypeCode(shared_from_this()); out+="("; for (int i=0; isize()); i++) { if (i) out+=", "; out+=(*subTypes)[i].type->getCppLiteral((char*)data+getSubType((*subTypes)[i].name).offset, prog); } out+=")"; return out; } size_t getSize() { size_t sum=0; for (auto i: *subTypes) { sum+=i.type->getSize(); } return sum; } PrimitiveType getType() { return TUPLE; } OffsetAndType getSubType(string name) { size_t offset=0; for (auto i: *subTypes) { if (i.name==name) return {offset, i.type}; else offset+=i.type->getSize(); } return {0, nullptr}; } vector* getAllSubTypes() { return &(*subTypes); } bool isWhatev() { return hasWhatev; } bool isCreatable() { return hasOnlyCreatable; } Type actuallyIs(Type target) { if (target->getType()!=TUPLE) { if ((*subTypes).size()==1) { return (*subTypes)[0].type->actuallyIs(target); } else { throw PineconeError("actuallyIs called on tuple with target that is not tuple", INTERNAL_ERROR); } } TupleTypeMaker maker; auto targetTypes=target->getAllSubTypes(); for (int i=0; i<(int)subTypes->size(); i++) { maker.add((*subTypes)[i].name, (*subTypes)[i].type->actuallyIs((*targetTypes)[i].type)); } return maker.get(true); } protected: bool matchesSameTypeType(Type other) { auto o=(TupleType*)(&(*other)); if (!isAnonymous && !o->isAnonymous) return false; if (subTypes->size()!=o->subTypes->size()) return false; for (int i=0; isize()); i++) { //if ((*subTypes)[i].name!=(*o->subTypes)[i].name) // return false; //if ((*subTypes)[i].type!=(*o->subTypes)[i].type) // return false; if (!(*subTypes)[i].type->matches((*o->subTypes)[i].type)) return false; } return true; } private: unique_ptr> subTypes; bool isAnonymous=true; bool hasWhatev=false; bool hasOnlyCreatable=true; }; class PtrType: public TypeBase { public: PtrType(Type in) { type=in; } string getString() { return "-> "+type->getString()+" (pointer)"; } string getCompactString() { return "Pp_"+type->getCompactString()+"_pP"; } string getCppLiteral(void * data, CppProgram * prog) { string name=to_string((long long)data); prog->declareGlobal(name, type, type->getCppLiteral(*(void**)data, prog)); string out="&"+prog->getGlobalNames()->getCpp(name); return out; } size_t getSize() { return sizeof(void*); } PrimitiveType getType() { return PTR; } Type getSubType() { return type; } Type actuallyIs(Type target) { return getSubType()->actuallyIs(target->getSubType())->getPtr(); } protected: Type type; bool matchesSameTypeType(Type other) { return ((PtrType*)(&(*other)))->type->matches(type); } }; class MetaType: public TypeBase { public: MetaType(Type in) { type=in; } string getString() { return "{"+type->getString()+"} (meta type)"; } string getCompactString() { return "Mm_"+type->getCompactString()+"_mM"; } string getCppLiteral(void * data, CppProgram * prog) { return "/* can't add meta type to C++ code */"; } size_t getSize() { return 0; } bool isCreatable() { return false; } PrimitiveType getType() { return METATYPE; } Type getSubType() { return type; } bool isWhatev() { return type->isWhatev(); } Type actuallyIs(Type target) { return getSubType()->actuallyIs(target->getSubType())->getMeta(); } protected: Type type; bool matchesSameTypeType(Type other) { return ((MetaType*)(&(*other)))->type==type; } }; class WhatevType: public TypeBase { public: WhatevType() {} string getString() { return "(whatev type)"; } string getCompactString() { return "W"; } bool isCreatable() { return false; } string getCppLiteral(void * data, CppProgram * prog) { throw PineconeError("getCppLiteral called on whatev type, wich should not have happened", INTERNAL_ERROR); } size_t getSize() { throw PineconeError("getSize called on whatev type, wich should not have happened", INTERNAL_ERROR); } PrimitiveType getType() { return WHATEV; } bool isWhatev() { return true; } Type getSubType() { throw PineconeError("getSubType called on whatev type, wich should not have happened", INTERNAL_ERROR); } Type actuallyIs(Type target) { return target; } protected: Type type; bool matchesSameTypeType(Type other) { return true; } }; Type TypeBase::makeNewVoid() { return Type(new VoidType); } Type TypeBase::makeNewPrimitive(PrimitiveType typeIn) { return Type(new PrimType(typeIn)); } Type TypeBase::makeNewWhatev() { return Type(new WhatevType()); } vector* TypeBase::getAllSubTypes() { throw PineconeError("getAllSubTypes called on type that was not a tuple", INTERNAL_ERROR); } const Type Unknown(new UnknownType); const Type Void = TypeBase::makeNewVoid(); const Type Whatev = TypeBase::makeNewWhatev(); const Type Bool = TypeBase::makeNewPrimitive(TypeBase::BOOL); const Type Byte = TypeBase::makeNewPrimitive(TypeBase::BYTE); const Type Int = TypeBase::makeNewPrimitive(TypeBase::INT); const Type Dub = TypeBase::makeNewPrimitive(TypeBase::DUB); Type String = nullptr; Type TypeBase::getMeta() { return Type(new MetaType(shared_from_this())); } Type TypeBase::getPtr() { if (!ptrToMe) ptrToMe=Type(new PtrType(shared_from_this())); return ptrToMe; } string TypeBase::getString(PrimitiveType in) { switch (in) { case UNKNOWN: return "UNKNOWN_TYPE"; case VOID: return "VOID"; case BOOL: return "BOOL"; case BYTE: return "BYTE"; case INT: return "INT"; case DUB: return "DUB"; case TUPLE: return "TUPLE"; case METATYPE: return "METATYPE"; default: return "ERROR_GETTING_TYPE"; } } bool TypeBase::matches(Type other) { if (other==shared_from_this()) return true; if (getType()==WHATEV || other->getType()==WHATEV) return true; if (getType()==TUPLE) { auto subTypes=getAllSubTypes(); if (subTypes->size()==1) { if ((*subTypes)[0].type->matches(other)) return true; } } if (other->getType()==TUPLE) { auto subTypes=other->getAllSubTypes(); if (subTypes->size()==1) { if ((*subTypes)[0].type->matches(shared_from_this())) return true; } } if (other->getType()!=getType()) return false; return matchesSameTypeType(other); } Type TypeBase::actuallyIs(Type target) { if (!matches(target)) { throw PineconeError("actuallyIs called with type that doesn't match", INTERNAL_ERROR); } if (isWhatev()) { throw PineconeError("actuallyIs not implemented properly for Whatev type", INTERNAL_ERROR); } return shared_from_this(); } Type makeTuple(const vector& in, bool isAnonymous) { auto ptr=unique_ptr>(new vector(in)); return Type(new TupleType(move(ptr), isAnonymous)); } TupleTypeMaker::TupleTypeMaker() { subTypes=unique_ptr>(new vector); } void TupleTypeMaker::add(string name, Type type) { if (subTypes) subTypes->push_back(NamedType{name, type}); else error.log(FUNC+"called after type has been created", INTERNAL_ERROR); } void TupleTypeMaker::add(Type type) { string name=getUniqueName(); if (!name.empty()) { add(name, type); } else { error.log("finding unique name in tuple type failed", INTERNAL_ERROR); } } Type TupleTypeMaker::get(bool isAnonymous) { if (subTypes) return Type(new TupleType(std::move(subTypes), isAnonymous)); else error.log(FUNC+"called after type has been created", INTERNAL_ERROR); return Void; } string TupleTypeMaker::getUniqueName() { for (char c='a'; c<='z'; ++c) { string str; str+=c; bool valid=true; for (auto i: *subTypes) { if (i.name==str) { valid=false; break; } } if (valid) return str; } error.log("you've gotta be fuckin kidding", SOURCE_ERROR); return ""; } ================================================ FILE: src/main.cpp ================================================ #include "../h/msclStringFuncs.h" #include "../h/PineconeProgram.h" #include "../h/ErrorHandler.h" #include using std::cout; using std::endl; using std::vector; using std::string; vector cmdLineArgs; struct Flags { string myPath; // path to the pinecone exeutable that is now running vector inFiles; // all the input files bool debug=false; // if to show debugging info bool help=false; // if to show help message bool version=false; // if to show version message bool runInterpreted=true; // if to run the program in the interpreter string cppOutFile=""; // output file for transpiled C++ code, empty if flag not set string binOutFile=""; // binary executable output file, empty if flag not set bool runCompiled=false; // if to run the program compiled bool flagError=false; // if to quit immediately, this is set if there is an unrecognised flag }; Flags getFlags(int argc, char ** argv); int main(int argc, char ** argv) { Flags flags=getFlags(argc, argv); if (flags.flagError) { cout << "try 'pinecone -h' for help" << endl; return 0; } if (flags.help) { cout << "Pinecone v" << VERSION_X << "." << VERSION_Y << "." << VERSION_Z << endl; cout << "usage: pinecone [options] [source file] [options]" << endl; cout << "options: " << endl; cout << "-v, -version display the version of Pinecone" << endl; cout << "-d, -debug display debugging info before running the program" << endl; cout << "-r, -run run the program with the interpreter" << endl; cout << " active by default if no transpiling commands are present" << endl; cout << " currently, anything after -r is ignored" << endl; cout << "-cpp [file] transpile to C++ and save the output in the given file" << endl; cout << "-bin [file] transpile, compile with GCC and save the binary" << endl; cout << "-e, -execute transpile, compile and execute the binary" << endl; cout << " any combination of -cpp, -bin and -e can be used" << endl; cout << " like -r, anything after -e is ignored" << endl; cout << "-h, -help display this help and quit" << endl; cout << endl; cout << endl; return 0; } if (flags.version) { cout << "Pinecone version " << VERSION_X << "." << VERSION_Y << "." << VERSION_Z << endl; return 0; } PineconeProgram program; if (flags.inFiles.empty()) { cout << "no source file specified" << endl; cout << "try 'pinecone -h' for help" << endl; return 0; } else if (flags.inFiles.size()>1) { cout << "multiple source files specified, Pinecone does not currently support this" << endl; cout << "try 'pinecone -h' for help" << endl; return 0; } program.resolveProgram(flags.inFiles[0], flags.debug); if (flags.runInterpreted) { if (error.getIfErrorLogged()) { if (flags.debug) cout << endl << ">>>>>> execution aborted due to previous error <<<<<<" << endl; else cout << "program not executed due to errors" << endl; } else { if (flags.debug) cout << endl << "running program..." << endl << endl; program.execute(); } } if (!flags.cppOutFile.empty() || !flags.binOutFile.empty() || flags.runCompiled) { string cppCode=program.getCpp(); if (error.getIfErrorLogged()) { if (flags.debug) cout << endl << ">>>>>> transpiling failed <<<<<<" << endl; else cout << "transpiling failed" << endl; } else { string cppFileName=flags.cppOutFile; if (cppFileName.empty()) cppFileName="tmp_pn_transpiled.cpp"; if (flags.debug) cout << endl << putStringInBox(cppCode, "C++ code", true, false, -1) << endl; writeFile(cppFileName, cppCode, flags.debug); if (!flags.binOutFile.empty() || flags.runCompiled) { string binFileName=flags.binOutFile; if (binFileName.empty()) binFileName="tmp_pn_compiled"; string cmd; cmd="g++ -std=c++11 '"+cppFileName+"' -o '"+binFileName+"'"; if (flags.debug) cout << "running '"+cmd+"'" << endl; runCmd(cmd, true); if (flags.runCompiled) { if (flags.debug) cout << endl; cmd = "./"+binFileName + " --running-from-pinecone " + str::join(cmdLineArgs, " ", false); if (flags.debug) cout << "running '"+cmd+"'" << endl << endl; runCmd(cmd, true); } if (flags.binOutFile.empty()) remove(binFileName.c_str()); } if (flags.cppOutFile.empty()) remove(cppFileName.c_str()); } } if (flags.debug) cout << endl << "all done" << endl; return 0; } Flags getFlags(int argc, char ** argv) { bool after = false; Flags flags; for (int i=1; i1 && arg[0]=='-') { string flag=arg.substr(1, string::npos); if (flag=="d" || flag=="debug") { flags.debug=true; } else if (flag=="v" || flag=="version") { flags.version=true; } else if (flag=="h" || flag=="help") { flags.help=true; } else if (flag=="r" || flag=="run") { flags.runCompiled=false; flags.runInterpreted=true; after = true; } else if (flag=="cpp") { if (i+1>=argc) { cout << "output file must follow '-cpp' flag"; flags.flagError=true; } flags.runInterpreted=false; flags.cppOutFile=string(argv[i+1]); i++; } else if (flag=="bin") { if (i+1>=argc) { cout << "output file must follow '-bin' flag"; flags.flagError=true; } flags.runInterpreted=false; flags.binOutFile=string(argv[i+1]); i++; } else if (flag=="e" || flag=="execute") { flags.runCompiled=true; flags.runInterpreted=false; after = true; } else { cout << "unknown flag '"+flag+"'" << endl; flags.flagError=true; } } else { flags.inFiles.push_back(arg); cmdLineArgs.push_back(arg); } } else { cmdLineArgs.push_back(arg); } } return flags; } ================================================ FILE: src/msclStringFuncs.cpp ================================================ #include "../h/msclStringFuncs.h" #include #include #include // for terminal size detection #include //#ifdef _WIN32 #ifdef __linux__ //#include #include // for terminal size detection #endif // __linux__ using std::stringstream; bool substringMatches(const string& in, int pos, const string& subStr) { //check if the substring extends past the string size if (in.size()& out) { int start=0; if (pattern.size()<1) return; while (start& in) { for (unsigned i=0; i=1) { return leftCap+in.substr(0, size-capSize-1)+"…"+rightCap; } else if (size-capSize>=0) { return leftCap+string("…").substr(0, size-capSize)+rightCap; } else { return (leftCap+rightCap).substr(0, size); } } else if (padSize==0) // input size was exactly right { return leftCap+in+rightCap; } else // need to pad { if (alignment==0) // center alignment { string leftPad, rightPad; for (int i=0; i0) // right align return leftCap+in+rightCap+padStr; else // left align return padStr+leftCap+in+rightCap; } } } string getTextOfLine(const string& in, int lineNum) { int start=-1; int end=-1; if (lineNum<1) { return ""; } else if (lineNum==1) { start=0; } int line=1; for (unsigned i=0; i=0) return in.substr(start, string::npos); else return ""; } string lineListToBoxedString(const vector& in, string boxName, int lineNum, bool alwaysWidthMax, int maxWidth) { string out; auto first=in.begin(); auto last=in.end(); if (first!=last) { last--; while(first!=last && *first=="") { first++; if (lineNum>=0) lineNum++; } while (first!=last && *last=="") { last--; } last++; } //the extra width of the padding on the right and left (not the virt lines) int extraWidth=(lineNum<0)?4:10; //the size of the contents (not the container) int size; if (alwaysWidthMax) { size=maxWidth-extraWidth; } else { size=boxName.size()-extraWidth+6; for (auto i: in) { size=max(size, int(i.size())); } size=min(maxWidth-extraWidth, size); } if (boxName=="") out+=" "+padString("", size+extraWidth, 1, "_")+" "; else out+=" _"+padString(boxName, size+extraWidth-2, 0, "_", "[ ", " ]")+"_ "; out+="\n |"+padString("", size+extraWidth, 1, " ")+"| "; auto i=first; while (i!=last) { if (lineNum<0) { out+="\n | "; } else { out+="\n |"+padString(to_string(lineNum), 4, -1)+" "; lineNum++; } out+=padString(*i, size, 1)+" | "; i++; } out+="\n |"+padString("", size+extraWidth, 1, "_")+"| \n"; return out; } string putStringInBox(const string& in, string boxName, bool showLineNums, bool alwaysWidthMax, int maxWidth) { vector lines; if (maxWidth<0) { maxWidth=getTermWidth()-4; } sliceStringBy(in, "\n", lines); tabsToSpaces(lines); string out=lineListToBoxedString(lines, boxName, showLineNums?1:-1, alwaysWidthMax, maxWidth); return out; } string putStringInTable(const string& in, string tableName) { vector lineStrs; sliceStringBy(in, "\n", lineStrs); vector> table; vector widths; //vector lines; for (auto i: lineStrs) { vector row; sliceStringBy(i, "\t", row); for (unsigned j=0; j0) out+="|"; out+=padString("", widths[j]+4, 1, " "); } //out+=" | \n | "+padString("", totalWidth+(table.back().size()-1)*5, 1, " ")+" | "; out+="| "; out+="\n | "; for (unsigned j=0; j0) out+=" | "; int alignment=(j==i.size()-1)?(1):((j==0)?(-1):0); out+=padString(i[j], widths[j], alignment); } out+=" | "; } out+="\n |"; for (unsigned j=0; j0) out+="|"; out+=padString("", widths[j]+4, 1, "_"); } out+="| \n"; return out; } string doubleToString(double in) { long long a=in; long long b=(in-a)*10000000000; if (b<0) b*=-1; if (b%10==9) b+=1; while (b>0 && !(b%10)) b/=10; return to_string(a)+"."+to_string(b); } int stringToInt(string in) { int out = 0; for (int i = 0; i < (int)in.size(); i++) { if (in[i] >= '0' && in[i] <= '9') { out = out * 10 + in[i] - '0'; } else if (in[i] == '.') break; } if (in.size() > 0 && in[0] == '-') out *= -1; return out; } double stringToDouble(string in) { double out = 0; int divider = 1; for (int i = 0; i < (int)in.size(); i++) { if (divider == 1) { if (in[i] >= '0' && in[i] <= '9') out = out * 10 + in[i] - '0'; else if (in[i] == '.') divider = 10; } else { if (in[i] >= '0' && in[i] <= '9') { out += (double)(in[i] - '0') / divider; divider *= 10; } } } if (in.size() > 0 && in[0] == '-') out *= -1; return out; } string loadEntireFile(string inName, bool printOutput) { std::fstream inFile; if (printOutput) cout << "attempting to open '" << inName << "'..." << endl; inFile.open(inName); if (!inFile.is_open()) { if (printOutput) cout << "'" << inName << "' failed to open :(" << endl; return ""; } else { if (printOutput) cout << "file opended, reading file..." << endl; stringstream strStream; strStream << inFile.rdbuf();//read the file string out = strStream.str();//str holds the content of the file inFile.close(); if (printOutput) cout << "file reading done, '" << inName << "' closed" << endl; return out; } } bool writeFile(const string& filename, const string& contents, bool debug) { std::ofstream outFile; if (debug) cout << "attempting to write to '" << filename << "'..." << endl; outFile.open(filename); if (!outFile.is_open()) { if (debug) cout << "'" << filename << "' failed to open :(" << endl; return false; } else { if (debug) cout << "file opended, writing to file..." << endl; outFile << contents; outFile.close(); if (debug) cout << "file reading done, '" << filename << "' closed" << endl; return true; } } /* //NOTE: I copied this from where I copied this from somewhere on the internet. no idea how or why it works. string runCmd(string cmd, bool printOutput) // if print output is false, nothing will be printed unil the entire command is done { const int bufferSize=4096; char buffer[bufferSize]; std::string result = ""; FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) throw std::runtime_error("popen() failed in getOutputFromCmd"); try { while (!feof(pipe)) { if (fgets(buffer, bufferSize, pipe) != NULL) { result += buffer; if (printOutput) { cout << buffer; cout.flush(); } } } } catch (...) { pclose(pipe); throw; } pclose(pipe); return result; } */ char getRandChar() { static unsigned int seed = 1; //seed = (unsigned)newseed & 0x7fffffffU; seed = (seed * 1103515245U + 12345U) & 0x7fffffffU; int num=seed%(26+26+10); if (num<26) { return num+'a'; } else if (num<26+26) { return num-26+'A'; } else if (num<26+26+10) { return num-26-26+'0'; } return '_'; // if I did my arithmetic right, this shouldn't happen } string getUniqueString(string hint, std::function checker, bool alwaysAppendRandom) { string out=hint; int attempts=0; bool invalid=out.empty() || !checker(out) || alwaysAppendRandom; while (invalid) { if (alwaysAppendRandom || attempts>=10) { out=hint+"_"; if (attempts>20) { throw PineconeError("could not find unique random name", INTERNAL_ERROR); } for (int i=0; i<3; i++) out+=getRandChar(); } else { string suffix=to_string(attempts); out=hint+"_"+suffix; } attempts++; invalid=!checker(out); } return out; } string runCmd(string cmd, bool printOutput) // if print output is false, nothing will be printed unil the entire command is done { std::string result = ""; FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) throw std::runtime_error("popen() failed in getOutputFromCmd"); try { while (!feof(pipe)) { char c; if ((c=getc(pipe)) != EOF) { result += c; if (printOutput) { cout << c; cout.flush(); } } } } catch (...) { pclose(pipe); throw; } pclose(pipe); return result; } // TODO: implement for windows int getTermWidth() { #ifdef __linux__ // reports inacurate size if the terminal is new, as is the case when running from an IDE static bool firstTime=true; if (firstTime) { usleep(20000); firstTime=false; } struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); //cout << "width measured as " << w.ws_col << endl; return w.ws_col; #else return 80; #endif } ================================================ FILE: src/utils/fileUtils.cpp ================================================ #include "../../h/utils/fileUtils.h" #include #include using std::stringstream; void loadFile(string filepath, string& contents) { std::fstream inFile; inFile.open(filepath); if (!inFile.is_open()) { throw "could not open '"+filepath+"'"; } else { stringstream strStream; strStream << inFile.rdbuf();//read the file contents = strStream.str();//str holds the content of the file inFile.close(); } } void writeFile(string filepath, const string& contents) { std::ofstream outFile; outFile.open(filepath); if (!outFile.is_open()) { throw "error writing to '"+filepath+"'"; } else { outFile << contents; outFile.close(); } } ================================================ FILE: src/utils/stringArray.cpp ================================================ #include "../../h/utils/stringArray.h" namespace str { int getMaxWidth(vector& in) { int out=0; for (int i=0; i<(int)in.size(); i++) { int w=getWidth(in[i]); if (w>out) out=w; } return out; } void padWidths(vector& out, int size, StringPadAlignment alignment, string pad, string leftCap, string rightCap) { if (size<0) { size=getMaxWidth(out); } for (int i=0; i<(int)out.size(); i++) { out[i]=str::pad(out[i], size, alignment, pad, leftCap, rightCap); } } string join(vector& in, string joiner, bool addAtEnd) { string out; for (int i=0; i<(int)in.size(); i++) { out+=in[i]; if (addAtEnd || i!=(int)in.size()-1) out+=joiner; } return out; } } ================================================ FILE: src/utils/stringDrawing.cpp ================================================ #include "../../h/utils/stringDrawing.h" #include "../../h/utils/stringArray.h" #include "../../h/msclStringFuncs.h" //#include namespace str { // https://en.wikipedia.org/wiki/Box-drawing_character // │ ─ ╭ ╮ ╯ ╰ string getBoxedString(const string& in, string boxName, bool showLineNums, bool alwaysWidthMax, int maxWidth) { vector lines; int width; if (maxWidth<0) { maxWidth=getTermWidth()-4; } str::splitByLine(lines, tabsToSpaces(in)); int startLineNum=1; while (!lines.empty() && lines[0].empty()) { lines.erase(lines.begin()); startLineNum++; } while (!lines.empty() && lines.back().empty()) { lines.pop_back(); } int nameWidth=getWidth(boxName); if (showLineNums) { int numChars=getWidth(to_string(lines.size()+startLineNum-1)); for (int i=0; i<(int)lines.size(); i++) { lines[i]=pad(to_string(i+startLineNum), numChars+5, ALIGNMENT_RIGHT, " ", "", " ")+lines[i]; } } if (alwaysWidthMax) { width=maxWidth; } else { width=std::min(std::max(str::getMaxWidth(lines), nameWidth+4-2), maxWidth); } str::padWidths(lines, width); for (int i=0; i<(int)lines.size(); i++) { lines[i]=" ┃ "+lines[i]+" ┃ "; } string bottom; string emptyLn; for (int i=0; i& leaves) { return "makeTreeSection not yet implemented"; } */ void putArrayInTreeNodeBox(vector& data) { int width=data.empty()?0:getWidth(data[0]); for (int i=0; i<(int)data.size(); i++) { data[i]="│ "+data[i]+" │"; } data.push_back("╰─"+pad("", width, ALIGNMENT_LEFT, "─")+"─╯"); data.insert(data.begin(), "╭─"+pad("┴", width, ALIGNMENT_CENTER, "─")+"─╮"); } string putStringInTreeNodeBox(const string& in) { vector ary; splitByLine(ary, in); putArrayInTreeNodeBox(ary); return join(ary); } string makeList(vector& data) { vector ary; for (int i=0; i elemAry; str::splitByLine(elemAry, data[i]); int xPos=str::getGlyphPosOf(elemAry[0], "┴"); if (xPos>=0) { ary.push_back("┠─"+str::pad("", xPos, str::ALIGNMENT_LEFT, "─")+"╮"); } for (auto i: elemAry) { ary.push_back("┃ "+i); } } padWidths(ary); for (int i=0; i leftAry; splitByLine(leftAry, leftLeaf); vector rightAry; splitByLine(rightAry, rightLeaf); /* if (rightAry.empty() && !leftAry.empty()) rightAry.push_back(padString("", getMaxWidth(leftAry))); if (leftAry.empty() && !rightAry.empty()) leftAry.push_back(padString("", getMaxWidth(rightAry))); if (leftAry.empty()) leftAry.push_back(""); */ // make sure left and right arrays are the same size while (leftAry.size() rootAry; splitByLine(rootAry, root); if (rootAry.empty()) rootAry.push_back(""); padWidths(rootAry); int rootWidth=getMaxWidth(rootAry); //int leafWidth=std::max(std::max(getMaxWidth(leftAry), getMaxWidth(rightAry)), rootWidth)+1; //padWidths(leftAry, leafWidth, ALIGNMENT_LEFT); //padWidths(rightAry, leafWidth, ALIGNMENT_RIGHT); // make sure everything is padded padWidths(leftAry); padWidths(rightAry); //int size=std::max(rootWidth, getMaxWidth(leftAry)+getMaxWidth(rightAry)); int leftPadWidth=0; // left connection line if (!leftLeaf.empty()) { int endXPos=getGlyphPosOf(leftAry[0], "┴"); int leftBranchWidth=getWidth(leftBranch); if (endXPos>0) { int leftMargin=0;//(getMaxWidth(leftAry)-leftBranchWidth)/2; if (leftMargin>0) { for (int i=0; i<(int)leftAry.size(); i++) { leftAry[i]=pad("", leftMargin)+leftAry[i]; } endXPos+=leftMargin; } int middleYPos=rootAry.size()/2; rootAry[middleYPos]="┤"+sub(rootAry[middleYPos], 1, -1); leftPadWidth=getMaxWidth(leftAry)-rootWidth/2; if (endXPos+1>leftPadWidth) leftPadWidth=endXPos+1; leftPadWidth=max(leftPadWidth, (int)ceil(leftBranchWidth/2.0)+endXPos); for (int i=0; i<(int)rootAry.size(); i++) { if (i0) { int middleYPos=rootAry.size()/2; rootAry[middleYPos]=sub(rootAry[middleYPos], 0, getWidth(rootAry[middleYPos])-1)+"├"; int inset=rootWidth/2; inset=min(endXPos-(int)floor(rightBranchWidth/2.0), inset); for (int i=0; i<(int)rootAry.size(); i++) { if (i // for memcpy namespace str { string charToCppStringLiteralEscaped(unsigned char c) { string out="\\"; for (int i=0; i<3; i++) { out+=c%8+'0'; c/=8; } return out; } string intToBase62(unsigned int num, int maxDigits) { int i=0; string out; while (num!=0 && (maxDigits<0 || i namespace str { void splitBy(vector& out, const string& in, const string& splitter, bool keepSplitter) { int i=0; int start=0; while (i<=(int)in.size()-(int)splitter.size()) { if (subMatches(in, i, splitter)) { if (keepSplitter) nextGlyph(i, in); out.push_back(in.substr(start, i-start)); if (!keepSplitter) nextGlyph(i, in); start=i; } else { nextGlyph(i, in); } } if ((int)in.size()!=start) out.push_back(in.substr(start, in.size()-start)); } string pad(const string& in, int size, StringPadAlignment alignment, string pad, string leftCap, string rightCap) { int capWidth=getWidth(leftCap)+getWidth(rightCap); int inSize=getWidth(in); int padSize=size-(inSize+capWidth); if (padSize<0) // need to chop { if (size-capWidth>=1) { if (alignment==ALIGNMENT_RIGHT) { return leftCap+"…"+sub(in, inSize-(size-capWidth-1), inSize)+rightCap; } else { return leftCap+sub(in, 0, size-capWidth-1)+"…"+rightCap; } } else { return sub(leftCap+rightCap, 0, size); } } else if (padSize==0) // input size was exactly right { return leftCap+in+rightCap; } else // need to pad { if (alignment==ALIGNMENT_CENTER) // center alignment { string leftPad, rightPad; for (int i=0; i+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." options: "" IS_TRANSPILED ? options: options+" -e" cmdOut: runCmd: "printf '"+symInput+"' | ./pinecone examples/brainfuck.pn" + options print: cmdOut ================================================ FILE: tests/integration/morse.pn ================================================ # Tests my text to morse code converter print: "enter some text or type quit: .--. .. -. . -.-. --- -. . \nenter some text or type quit: .-.. .- -. --. \nenter some text or type quit: \n\n_____" symInput: "pinecone\\nlang\\nquit" options: "" IS_TRANSPILED ? options: options+" -e" cmd: "printf '"+symInput+"' | ./pinecone examples/morse.pn" + options print: runCmd: cmd ================================================ FILE: tests/integration/queue.pn ================================================ # this is a copy of the original file used for an intagration test # this is an implementation of a queue using a dynamically sized circular buffer # this was wretten for Pinecone v0.3, which does not yet have references, so a single global queue is used instead of a queue object print: "1\n2\n3\n4\n5\n6\nqueue {\n 7\n 8\n 9\n}\n\n_____" q: Queue main main :: {}: ( enqueue: 1 enqueue: 2 enqueue: 3 enqueue: 4 enqueue: 5 enqueue: 6 print: dequeue print: dequeue print: dequeue print: dequeue enqueue: 7 enqueue: 8 enqueue: 9 print: dequeue print: dequeue print: q ) Queue :: {array: IntArray, offset: Int, len: Int} Queue :: {} -> {Queue}: ( (IntArray: 1), 0, 0 ) resize :: {Int}: ( newArray: IntArray: in i: 0 | i= q.array.len ? resize: q.array.len*2 q: q.array, q.offset, q.len+1 tmp: q.array tmp.set: (q.offset+q.len-1)%q.array.len, in ) dequeue :: {} -> {Int}: ( tmp: q.array out: tmp.get: q.offset%q.array.len q: q.array, q.offset+1, q.len-1 q.len < q.array.len/3+1 ? resize: q.array.len/3+1 out ) peek :: {}: ( tmp: q.array tmp.get: q.offset%q.array.len ) print :: {Queue}: ( print: "queue {" i: 0 | i "+out | i = (in.offset+in.len-1)%in.array.len ? out: " -| "+out | out: " "+out print: out ) print: "}" print \\ ) ================================================ FILE: tests/readme.md ================================================ This directory holds all tests for the Pinecone language. To run tests, open the parent directory in terminal and run `./pinecone tests/run_tests.pn`. It is important that your current working directory is the parent directory and that there is a pinecone executable in the parent directory called `pinecone`. ================================================ FILE: tests/regression/bool_short_circuit.pn ================================================ # Tests if short circuit is applied to operations with multiple bools print: "1\nmyFunc called\nmyFunc called\nmyFunc called\nmyFunc called\n4\n_____" tru || (myFunc: tru) ? print: 1 fls && (myFunc: tru) ? print: 2 !(myFunc: tru) || fls ? print: 3 (myFunc: fls) || (myFunc: fls) || (myFunc: tru) || (myFunc: fls) ? print: 4 myFunc :: {Bool} -> {Bool}: ( print: "myFunc called" in ) ================================================ FILE: tests/regression/cpp_bad_escape_seq.pn ================================================ # Bad escape sequences can get into C++ transpiled code if a special char is followed by a hex digit tab: "\t" print: tab + "axyz\n_____" print: "\taxyz" ================================================ FILE: tests/regression/cpp_global_and_local_same_name.pn ================================================ # it gets confused when global and local vars have the same name print: "3\n3\n_____" a: 7 func :: {a: Int, b: Int}: ( a: in.a print: a ) func: 3, 3 print: a ================================================ FILE: tests/regression/error_on_nonexistant_file.pn ================================================ # This test attempts to run the Pinecone interpreter on a nonexistant file, which should produce an error print: "error: could not open 'thisFileDoesNotExist.pn'\nprogram not executed due to errors\n\n_____" cmd: "./pinecone thisFileDoesNotExist.pn" output: runCmd: cmd print: output ================================================ FILE: tests/regression/function_with_one_named_arg.pn ================================================ # test when there is a function that only has one argument which is named print: "8\n_____" myFunc: 8 myFunc :: {arg: Int}: ( print: in.arg ) ================================================ FILE: tests/regression/if_as_ternary.pn ================================================ # tests the if statement as a ternary operator, I think problem is if return type may always be void print: "yes\n_____" a: tru print: (a ? "yes" | "no") ================================================ FILE: tests/regression/multiple_left_inputs.pn ================================================ # tests when there are multiple left inputs to a function # original but was caused to failing to skip {} in parsing an a.b: c structure print: "7\ntru\n_____" func :: {Int, Bool}.{}: ( print: me.a print: me.b ) (7, tru).func ================================================ FILE: tests/regression/negative_literal.pn ================================================ # this tests negitive literal numbers print: "-3\n-4.7\n9\n_____" print: -3 print: -4.7 print: 7-(-2) ================================================ FILE: tests/regression/no_newline_at_end.pn ================================================ # Pinecone test for source file without newline at the end of the file # IMPORTANT: many editors will automatically insert a newline on save, so don't edit this file unless you know what you're doing print: "es\n_____" a: "test" print: a.sub: 1, 3 ================================================ FILE: tests/run_tests.pn ================================================ pthslsh: "/" OS_IS_WINDOWS ? pthslsh: "\\" errorDetails: String print: "pinecone v"+VERSION.x.String+"."+VERSION.y.String+"."+VERSION.z.String + " tests" print runTestFolder: "unit", tru runTestFolder: "regression", tru runTestFolder: "integration", tru runTestFolder: "unfixed", fls print: "tests done" errorDetails.len > 0 ? ( print print: "details" print print: errorDetails ) runTestFolder :: {folder: String, printDetails: Bool}: ( print: (asciiEscape: 1) + in.folder + (asciiEscape: 0) print: (asciiEscape: 2) + (asciiEscape: 3) + "interpreted transpiled name " + (asciiEscape: 0) filepath: "tests" + pthslsh + in.folder cmd: String OS_IS_UNIX ? ( cmd: "ls " + filepath + pthslsh + " | grep .pn" )|( cmd: "dir /B " + filepath + " | FINDSTR /C:.pn" ) allFiles: runCmd: cmd i: 0 j: 0 | j < allFiles.len | j: j+1 @ ( j = allFiles.len-1 || (allFiles.sub: j, j+1) = "\n" ?( name: allFiles.sub: i, j runTest: filepath + pthslsh + name, (name.sub: 0, name.len-3), in.printDetails i: j+1 ) ) print ) runTest :: {filepath: String, name: String, showDetails: Bool}: ( cmdInput: "./pinecone " + in.filepath resultR: splitActualAndTarget: runCmd: cmdInput + " -r" resultE: splitActualAndTarget: runCmd: cmdInput + " -e" summeryLine: String summeryLine: summeryLine + " " summeryLine: summeryLine + (singleTest: resultR.actual, resultR.target, in.name + " (interpreted)", in.showDetails) summeryLine: summeryLine + " " summeryLine: summeryLine + (singleTest: resultE.actual, resultE.target, in.name + " (transpiled)", in.showDetails) summeryLine: summeryLine + " " summeryLine: summeryLine + in.name print: summeryLine ) singleTest :: {actual: String, target: String, label: String, expectSuccess: Bool} -> {String}: ( out: String in.actual = in.target ? ( out: (asciiEscape: 1) + " . " + (asciiEscape: 0) )|( in.expectSuccess ? out: (asciiEscape: 91) + "XXX" + (asciiEscape: 0) | out: "XXX" in.expectSuccess ? ( errorDetails: errorDetails + (getDiff: in.actual, in.target, in.label) ) ) out ) getDiff :: {actual: String, target: String, label: String} -> {String}: ( actual: asciiEscape: 0 target: asciiEscape: 0 isMatching: tru i: 0 | i {String}: ( (27).ascii + "[" + in.String + "m" ) splitActualAndTarget :: {String} -> {actual: String, target: String}: ( quit: fls i: 0 target: String actual: String !quit @ ( i + targetActualSplit.len >= in.len ? ( target: "no target\n" in.len = 0 ? actual: "no output\n" | actual: in quit: tru ) | (in.sub: i, i+targetActualSplit.len) = targetActualSplit ? ( target: in.sub: 0, i actual: in.sub: i+targetActualSplit.len, in.len quit: tru )|( i: i+1 ) ) actual, target ) targetActualSplit :: "_____\n" divideLine :: (asciiEscape: 2) + "==========================================================\n" + (asciiEscape: 0) ================================================ FILE: tests/unfixed/backslash_in_block_comment.pn ================================================ # test with backslash in a block comment print: "this should print\nthis should also print\n_____" print: "this should print" // print: "inside a comment, so this should not print" print: "backslashes line \\ in string literal should not break block comment" \\ print "this should also print" ================================================ FILE: tests/unit/conditionals.pn ================================================ # Pinecone conditionals test print: "3\n5\n_____" a: fls b: 10 a ? print: 1 |( b<3 ? ( print: 2 )|( print: 3 6 = 9 ? print: 4 | print: 5 ) ) ================================================ FILE: tests/unit/constants.pn ================================================ # Pinecone constants test print: "21\n_____" print: c*3 a :: 5 c :: a+b b :: 2 ================================================ FILE: tests/unit/destroyers_and_copiers.pn ================================================ # tests the calling of destroyers and copiers print: "copier called\ncopier called\n3\ndestroyer called\ndestroyer called\n_____" MyStruct :: {Int, Dub} MyStruct :: {} -> {MyStruct}: 3, 4.7 __destroy__ :: {MyStruct}: ( print: "destroyer called" ) __copy__ :: {MyStruct} -> {MyStruct}: ( print: "copier called" in ) ( myStruct: MyStruct a: myStruct print: a.a ) ================================================ FILE: tests/unit/funcs.pn ================================================ # Pinecone funcs test print: "1\n6\n10\n5\n3\n2\n1\n0\n15\n_____" global: 14 func0 print: func1: 8 print: func2: 1, 9 print: 2.func3: 3 func4: 3 func5 func0 :: {}: ( print: 1 ) func1 :: {Int} -> {Int}: ( in-2 ) func2 :: {v1: Int, v2: Int} -> {Int}: ( in.v1+in.v2 ) func3 :: {Int}.{Int} -> {Int}: ( me+in ) func4 :: {Int}: ( print: in in > 0 ? func4: in-1 ) func5 :: {}: ( global: global+1 print: global ) ================================================ FILE: tests/unit/generate_windows_line_endings.sh ================================================ printf '# Pinecone Windows line endings test\r\nprint: "good\n_____"\r\n\r\nprint: "good"' > windows_line_endings.pn ================================================ FILE: tests/unit/loops.pn ================================================ # Pinecone loops test print: "4\n3\n2\n1\n\n0\n1\n2\n3\n4\n5\n_____" i: 4 i>0 @ ( print: i i: i-1 ) print j: 0 | j<6 | j: j+1 @ ( print: j ) ================================================ FILE: tests/unit/operators.pn ================================================ # Pinecone operators test print: "5\n4.75\n6\n1\n4\n2\n2.75\n2\nfls\nfls\ntru\ntru\ntru\nfls\ntru\nfls\n_____" print: 2+3 print: 1.5+3.25 print: 2*3 print: 3-2 print: 12/3 print: 11/4 print: 11.0/4.0 print: 17%5 print: 6=4 print: 9!=9 print: 6>4 print: 7<=7 print: tru && 4=4 print: tru && 4=2 print: tru || 4=2 print: !tru || 4=2 ================================================ FILE: tests/unit/order_of_ops.pn ================================================ # Pinecone order of ops test print: "11\n3\n6\ntru\n_____" print: 4+2*3+1 print: 4-2+1 print: 1+8%5+2 print: 8>2=5.3<=7.9 ================================================ FILE: tests/unit/strings.pn ================================================ # Pinecone strings test print: "hello\na\nbxyz\nwe\n6\n4\n89.342\ntru\nfls\nfls\n_____" myStr: "hello" print: myStr print: "a\nb"+"xyz" print: "qwerty".sub: 1, 3 print: "qwerty".len print: 4.String print: 89.342.String print: "abc" = "abc" print: "abc" = "xyz" print: "abc" = "abcd" ================================================ FILE: tests/unit/type_info.pn ================================================ # tests various funcs that return info about types print: "String\nInt\n1\n1\n2\n0\n_____" print: "hay".typeName print: Int.typeName print: tru.typeSize print: Byte.typeSize print: (Bool, Byte).typeSize MyStruct :: { Int Dub Bool } MyStruct :: {} -> {MyStruct}: (Int, Dub, Bool) print: MyStruct.typeSize - Int.typeSize - Dub.typeSize - Bool.typeSize ================================================ FILE: tests/unit/vars.pn ================================================ # Pinecone vars test print: "5.88\n_____" a: 3 b: 7.2 print: (Dub: a)+b/2.5 ================================================ FILE: tests/unit/whatevs.pn ================================================ # tests use of the Whatev type print: "9\n3.2\nhay\n4.7\n4_\nfls_\n8.3\n4.7\nayy\ntru\n1.9\ntru\nyo\n5\ntru\n9\n7.1\nwaddup\n_____" func0: 9 func0: 3.2 func0: "hay" func0: 4.7 4.func1 fls.func1 func2: 8.3, 4.7 func2: "ayy", tru func2: 1.9, tru func3: Struct0: "yo" print: func4 print: func5: 7.1 print: func5: "waddup" func0 :: {Whatev}: ( print: in ) func1 :: {Whatev}.{}: ( print: me.String + "_" ) func2 :: {abc: Whatev, xyz: Whatev}: ( print: in.abc print: in.xyz ) Struct0 :: { foo: Whatev bar: Int } Struct0 :: {Whatev} -> {Struct0}: ( in, 5 ) func3 :: {Struct0}: ( print: in.foo print: in.bar ) func4 :: {} -> {Whatev}: ( print: tru 9 ) func5 :: {Whatev} -> {Whatev}: ( in ) ================================================ FILE: tests/unit/windows_line_endings.pn ================================================ # Pinecone Windows line endings test print: "good _____" print: "good" ================================================ FILE: tutorials/0_setting_up.md ================================================ # Setting up your environment Disclamer: these steps work on Linux, and usually on OSX. Windows is not yet supported. For updates on Windows support, see [this issue](https://github.com/william01110111/Pinecone/issues/2). 1. Open a terminal and navigate to where you want the Pinecone folder to go 2. Run the command `git clone https://github.com/william01110111/Pinecone.git`. This will download the Pinecone source code 3. Move into the Pinecone directory 4. run the command `make`. If you get a _command not found_ error, you may need to install make or g++. 5. If all goes well, after a few seconds you should end up with a Pinecone executable in your current directory called `pinecone`. 6. Run it with the command `./pinecone path/to/pinecone/source.pn`. If you don't have any Pinecone source code yet, test it with `./pinecone examples/hello_world.pn` 7. If it works, you can now move on to the "basic concepts" tutorial [index](index.md) | [next: Basic Concepts ->](1_basic_concepts.md) ================================================ FILE: tutorials/1_basic_concepts.md ================================================ # Basic Concepts The tutorials for most programming languages start with a hello world program, but Pinecone isn't most programming languages. Instead, lets start with a program that prints out something far more important. ``` print: 42 ``` That's all you need in your file. No includes or boilerplate code of any sort. Just save that as a normal text file with the extension `.pn` and run it. ## Primitive Types 42 is an example of an __Int__, which is the same as an int in C++. It can hold positive and negative whole numbers. The other two primitive data types are Dub and Bool. __Dub__ is the same as a C++ double. It can hold decimal numbers. a Dub literal either ends with a d (ex: `47d`) or has a decimal point (ex: `47.0`). _NOTE: while a Dub literal can start with a decimal point, it can not end with one (ex: `.5` is valid but `5.` is not )_. A __Bool__ can only be `tru` or `fls`. If you think it should be `true` and `false` instead, you can email your complaints to williamwold@idontgiveafuck.com. A __String__ isn't really a primitive data type, but it is very common so I'll include it here anyway. It is a series of letters, numbers or other characters of any length. You make a string by surrounding text in double quotes. ## Operators In general, operators in Pinecone work the same as in any other language. It has all the ones you would expect with sensible order of operations. The following are the only major differences between operators in Pinecone and C-style langauges: * The assignment operator is `:` instead of `=`. * The equality operator is `=` instead of `==`. * There are no bitwise operators, they may be implemented at some point. * The short circuit aspect of boolean operators doesn't work yet, so `fls && functionReturningBool` will execute functionReturningBool. This will be fixed soon. ## Calling Functions `print: 42` is an example of a function call. __print__ is a standard function that prints what it is sent, followed by an newline. the ':' indicates that you want to sent input to print and whatever follows is the input. Functions can only take input of the exact type they are supposed to take (implicit casting will eventually be but is not yet implemented). Functions can, however be overloaded. This means there are multiple functions with the same name that are differentiated by what type their input is. print is an overloaded function, so you can also do `print: 42.1`, or even just `print` and it will compile (the latter takes no input and so it will just print an empty line). Functions can also take input from the left, but we will get to that later. ## Variables A __variable__ is a place you can store a value. Every variable has a type, but types are deduced implicitly. To create, change and use a variable, simply do the following: ``` myVarName: 88 myVarName: 12 print: myVarName ``` `myVarName` can be any series of letters, digits and underscores, as long as it doesn't start with a number. Variable names are case sensitive. As you can see, creating a variable, setting a variable and calling a function with all look the same. This is a very important feature for the language, as it makes refactoring variables into calculations much easier. ## Tuples A Tuple can be thought of as an ad hoc data structure. To construct one you simple combine several expressions with commas. The names of the elements of a tuple are `a`, `b`, `c`, etc. Elements can be accesed with the `.` operator. Here is an example: ``` myTuple: 6, 2.9, fls print: myTuple.c print: myTuple.a ``` The output of this will be ``` > fls > 6 ``` Tuple elements are supposed to be mutable, but this functionality is currently broken. ## Constants A __constant__ is a value that is determined at compile time. Constants are created with the constants assignment operator `::`. You can declare constants above or below where you use them. Trying to set a constants more then once will result in a compile time error. Here is an example of a simple use of constants: ``` a :: 3 print: b b :: a+c c :: 2 ``` This will compile correctly and print 5. ## Comments Comments are parts of your program that the compiler doesn't look at, so you can write notes and whatnot. In Pinecone, single-line comments start with a `#`. Multi-line comments start with `//` and end with `\\`. ## Explicit Type Conversion Sometimes, you might want to send a value to a variable or function of a different type. In the future, this will just work, but for now, you must convert it explicitly. To do this, call the target type's name like a function. For example: ``` myDub: 12.7 myDub: 32 # won't work because 32 is an Int myDub: Dub: 32 # explicit conversion ``` [index](index.md) | [next: Control Flow ->](2_control_flow.md) ================================================ FILE: tutorials/2_control_flow.md ================================================ # Control Flow In Pinecone, control flow is done with symbols instead of keywords. There are two symbols used for control flow, `?` and `@`. `|` is used in conjunction with one of them. ## If/Then `?` is used for ifs. You may be familiar with the ternary operator in C like languages. In Pinecone `?` can be used in much the same way (the only difference is `|` is used instead of `:`), but `?` is also used for all if statements with `|` being the 'else' operator. The body of an if statement is usually enclosed in parentheses, but this is optional if there is only one line in it. For 'else if', follow an else with another if statement. Here is an example: ``` a: tru b: 8 a ? print: 1 b<4 ? print: 2 | print: 3 a && b<7 ? ( print: 4 ) | b=8 ? ( print: 5 )|( print: 6 ) ``` The output of that code is ``` > 1 > 3 > 5 ``` ## loops `@` is the loop operator. It is used for while and for loops. A simple while loop is below: ``` i: 8 i>=0 @ ( print: i i: i-1 ) ``` Like if, the perenthesis would be optional if there was only one statement in the body. For loops are are based on C for loops, in that the header consits of 3 expressions, the first sets it up, the second checks if to continue each iteration and the third increments a vairable. The expressions are seporated with `|`. Here is an example: ``` i: 0 | i<12 | i: i+1 @ ( print: i ) ``` The output of this is the numbers 0-11. [index](index.md) | [next: Strings and Input ->](3_strings_and_input.md) ================================================ FILE: tutorials/3_strings_and_input.md ================================================ # Strings and Input In Pinecone, like in many langauges, a string is a series of characters of any length. To make a string, simply suround some text in double quotes: ``` myString: "this is a string!!!" ``` ## Operators and Functions Like other types, there are various operators and functions that can be applied to strings. * `=` checks if two strings are exactly the same. * `+` combines two strings. * `stringName.len` returns the length of that string. * `68.ascii` will return a string one character long containing the ascii value of the int. * `stringName.sub: start, end` retuns a substring. I use start and end instead of start and length because start and length is stupid. * `84.String` will return a string with that number in base 10 (aka, normal). This can be done for Ints, Dubs and Bools. ## String input You can get a string as an input from the user. To do this, call `String.input`. This will block execution until they type sonething in and press enter, at which time the finction will return a string. If you want to give the user a prompt, call the function like this: `"please enter some input: ".input`. [index](index.md) | [next: Structures and Functions ->](4_structures_and_functions.md) ================================================ FILE: tutorials/4_structures_and_functions.md ================================================ # Structures and Functions ## Types Since types for variables are implicit, you usually don't have to specify them. The exception is when creating your own data structures and functions, when types are always enclosed in {}. ## Data Structures Structures must be constants, so they are always delared with `::`. Your new type should be one or more other types inclosed in {}. Here is an example: ``` myType :: {Int, Dub} ``` The comma that seporates them is only for readability. It could be a semicolon, newline or just a space (NOTE: inside types is the only place a comma is interchangable with anything else). The elements in a data structure always have names. If none is specified, they are given the names `a`, `b`, `c`, etc. like tuples. You can specify your own names with `:`. For example: ``` myType :: { name1: Int name2: Bool } ``` ## Functions Functions must currently be declared as constants. In the future, non constant functions (aka lambda expressions) will be possible. The types a function takes and returns must be explicity stated in the header. A function header looks like this `{leftInput}.{rightInput} -> {returnType}`. The header should be followed by a colon and a perenthesis enclosed list of statements. ### Input and Output The left input and return type can be omitted, in wich case they are `Void`. If you want the right input to be `Void`, simply leave the {} empty. Right input is the type of input you are already used to. Left input is a concept that is somewhat unique to Pinecone. It is just like right input, except that you call the function with `input.function` instead of `function: input`. A function can take both left and right input. The right input is named `in` and the left is named `me`. There is currently no return syntax in Pinecon, instead return value is the last expression in the function. ### Examples ``` # declaring functions (can be done above or below calling them) addOne :: {Int} -> {Int}: ( a: in+1 a ) printNumbers :: {val1: Dub, val2: Dub}: ( print: in.val1 print: in.val2 ) leftInput :: {Int}.{} -> {String}:( print: me "return str" ) noArgs :: {}: ( print: "Hello" ) # calling functions print: addOne: 8 printNumbers: 7, 12 print: 8.leftInput noArgs ``` The result of this code is ``` 9 7.0 12.3 8 return str Hello ``` I know the `in.` is annoying. It will be made implicit soon. ## Overloading You can make multiple functions with the same name as long as they take different types. This is called overloading. Overloading operators is not yet supported, but it will be. [index](index.md) | [next: Whatevs ->](5_whatevs.md) ================================================ FILE: tutorials/5_whatevs.md ================================================ # Whatevs ## The Problem Sometimes explicitly declaring a type is inconvenient. You don't want a function to always take an Int, String or any other specific type. You just want it to take whatev. Thats why Pinecone has the Whatev type. ## How it Works Whatevs in Pinecone work very similar to templates and generics in other languages. You declare a function that takes a Whatev, and then the compiler makes different versions of that function for each type you try to send into it. Note that Whatevs do not enable dynamic typing at runtime. You can not make a Whatev variable except as an argument to a function. ## Using Whatevs The syntax is amazingly simple. Whatev is just a type, so you can use it like so: ``` # take any type, convert it to a string and return it wrapped in parenthesis putInPeren :: {Whatev} -> {Whatev}: ( "(" + in.String + ")" ) print: putInPeren: 8 print: putInPeren: tru ``` The output of this code is ``` (8) (tru) ``` Note that all types you use this function with must have an overload of the .String function defined, else you'll get a compile time error. ## Whatevs in Structs Whatevs can be used in structs. here is an example: ``` # define the struct MyWhatevStruct :: { foo: Int bar: Whatev } # define the constructor makeMyWhatevStruct :: {a: Int, b: Whatev} -> {MyWhatevStruct}: ( in.a, in.b ) # define the print function for makeMyWhatevStruct print :: {MyWhatevStruct}: ( print: in.foo.String + ", " + in.bar.String ) # use the struct a: makeMyWhatevStruct: 8, 9.3 b: makeMyWhatevStruct: 2, fls print: a print: b ``` The output of this is ``` 8, 9.3 2, fls ``` [index](index.md) | [next: Transpiling to C++ ->](6_transpiling_to_cpp.md) ================================================ FILE: tutorials/6_transpiling_to_cpp.md ================================================ # Transpiling to C++ Up until now you have been running your Pinecone code the default way, which is currently with the interpreter. Pinecone can also transpile your code to C++. Here I will be explaining how to do that and what the pros and cons of transpiling are. ## What is Interpreting, Compiling and Transpiling? To compile is to turn one language into another. Generally this means turn a mid level human readable language (C, C++, Rust, etc.) into complex-for-us-but-easy-for-computers machene code that can be executed. When a language (such as Python, Ruby, JavaScript, etc.) is interpreted, the interpreter walks through the source code line by line and runs it as it goes. There are pros and cons of both compiling and interpreting. Generally, interpreting gives more flexibility while compiling gives more performence. Finally, transpiling is technically a subset of compileing. It is converting between two languages of roughly equal complexity. For example, you may want to transpile from a newer version of JS to an older one for compatibility reasons. ## How This Relates to Pinecone Pinecone has been designed from the begining with the restraints of a compiled langauge in mind. In the begining I implemented the language as interpreted, and the interpreter is still the default way to run Pinecone, but now you can also transpile to C++. This gives your peograms a huge speed boost. The interpreter is default because it doesn't need to write files to disk, it doesn't need an external compiler and there are some known rare cases where transpileing doesn't work. ## Why Transpile to C++ Instead of Compiling Directly I tried to write a compiler with LLVM, but it was hard so I gave up. ## Using the Transpiler There are three command line arguments relevent transpiling `-cpp`, `-bin` and `-e`. These arguments can be used any combonation and if any are preasent, transpiling will be used instead of interpreting. `-cpp` and `-bin` should each be followed by a filepath which the transpiled source code and compiled binary executable will be saved to repectivly. `-e` should be used last, as subsequent arguments will be ignored. It means to actually execute the program, rather then just saving the output files. If cpp or bin file is not specified, they will be saved to a temporary file in your current directory which should be deleted automatically when the program is done. For compiling to a binary executable, and thus executing, the GCC compiler must be installed on your system. [index](index.md) | [next: Temporary Hacks ->](7_temporary_hacks.md) ================================================ FILE: tutorials/7_temporary_hacks.md ================================================ # Temporary Hacks Pinecone is still under rapid development, and there are a lot of features that are not yet implemented. This tutorial explains what is coming and what you can use until then. many of the functions described here may be depricated/removed as soon as better replacements are ready. ## Pass-by-reference There is not yet any pass-by-reference in Pinecone (with the exception of Int arrays and strings (though the latter is immutable)). Without pass-by-reference, you can't really have classes because functions can not modify the object they are called on. ## Arrays I have some very cool ideas for array syntax, but it is not ready yet. For now, you can have an array of ints with the built in class IntArray. Here is an example of how to use it: ``` myArray: IntArray: 6 # 6 is the length of the array i: 0 | i "path/to/target/file/from/current/file.pn" ``` I may keep this syntax, but I'm not sure so I'm keeping it in this tutorial. ## Linking with C++ A highly suggested feature has been the ability to integrate Pinecone into C++ projects. There are a number of ways to do this with different trade offs, and hopefully there will be a complete solution at some point. In the mean time, there is a way to use 3rd party libraries with Pinecone via two functions, `cppCode` and `cppHead`. To use these you **must** transpile instead of interpret. `cppCode` lets you drop C++ source code directly into the transpiler output. `cppHead` is the same except it adds its code to the top of the source file (outside any function). It is useful for includes. Here is an example: ``` cppHead: "#include " cppHead: "using std::cout;" cppHead: "using std::endl;" a: 9 cppCode: "cout << a << endl;" cppCode: "a = 2;" print: a ``` When you transpile and run this, the output produced is ``` 9 2 ``` You can not get a return value from `cppCode`, nore can you declare a variable within it that is usable elsewhere. To get data out, you must create a variable of the correct type beforehand and set that variable within the C++ code. There is no way to call Pinecone functions from C++ code. Don't try to fine a workaround, it won't work. If you want to link with 3rd party libraries, you must transpile to a .cpp file and do the linking and compiling yourself. ## Command Line Arguments Use `argLen` to get the number of arguments and `arg: argIndex` to get the string value of one. For example, here is a simple program to list all arguments: ``` print: "there are " + argLen.String + " args." i: 0 | i < argLen | i: i + 1 @ ( print: arg: i ) ``` To send args into a program, run the program with the `-r` or `-e` flag after the Pinecone source file name. All arguments after that will be sent in. The Pinecone source filepath will be the first arg. You can also compile the program and pass args in normally when running it without Pinecone. [index](index.md) ================================================ FILE: tutorials/index.md ================================================ # Pinecone Tutorial Index [About Pinecone](../readme.md) [0. Setting Up](0_setting_up.md) [1. Basic Concepts](1_basic_concepts.md) [2. Control Flow](2_control_flow.md) [3. Strings and Input](3_strings_and_input.md) [4. Structures and Functions](4_structures_and_functions.md) [5. Whatevs](5_whatevs.md) [6. Transpiling to C++](6_transpiling_to_cpp.md) [7. Temporary Hacks](7_temporary_hacks.md)