1d80f118eSChris Lattner============================================================ 2d80f118eSChris LattnerKaleidoscope: Extending the Language: User-defined Operators 3d80f118eSChris Lattner============================================================ 4d80f118eSChris Lattner 5d80f118eSChris Lattner.. contents:: 6d80f118eSChris Lattner :local: 7d80f118eSChris Lattner 8d80f118eSChris LattnerChapter 6 Introduction 9d80f118eSChris Lattner====================== 10d80f118eSChris Lattner 11d80f118eSChris LattnerWelcome to Chapter 6 of the "`Implementing a language with 12d80f118eSChris LattnerLLVM <index.html>`_" tutorial. At this point in our tutorial, we now 13d80f118eSChris Lattnerhave a fully functional language that is fairly minimal, but also 14d80f118eSChris Lattneruseful. There is still one big problem with it, however. Our language 15d80f118eSChris Lattnerdoesn't have many useful operators (like division, logical negation, or 16d80f118eSChris Lattnereven any comparisons besides less-than). 17d80f118eSChris Lattner 18d80f118eSChris LattnerThis chapter of the tutorial takes a wild digression into adding 19d80f118eSChris Lattneruser-defined operators to the simple and beautiful Kaleidoscope 20d80f118eSChris Lattnerlanguage. This digression now gives us a simple and ugly language in 21d80f118eSChris Lattnersome ways, but also a powerful one at the same time. One of the great 22d80f118eSChris Lattnerthings about creating your own language is that you get to decide what 23d80f118eSChris Lattneris good or bad. In this tutorial we'll assume that it is okay to use 24d80f118eSChris Lattnerthis as a way to show some interesting parsing techniques. 25d80f118eSChris Lattner 26d80f118eSChris LattnerAt the end of this tutorial, we'll run through an example Kaleidoscope 27d80f118eSChris Lattnerapplication that `renders the Mandelbrot set <#kicking-the-tires>`_. This gives an 28d80f118eSChris Lattnerexample of what you can build with Kaleidoscope and its feature set. 29d80f118eSChris Lattner 30d80f118eSChris LattnerUser-defined Operators: the Idea 31d80f118eSChris Lattner================================ 32d80f118eSChris Lattner 33d80f118eSChris LattnerThe "operator overloading" that we will add to Kaleidoscope is more 34d80f118eSChris Lattnergeneral than in languages like C++. In C++, you are only allowed to 35d80f118eSChris Lattnerredefine existing operators: you can't programmatically change the 36d80f118eSChris Lattnergrammar, introduce new operators, change precedence levels, etc. In this 37d80f118eSChris Lattnerchapter, we will add this capability to Kaleidoscope, which will let the 38d80f118eSChris Lattneruser round out the set of operators that are supported. 39d80f118eSChris Lattner 40d80f118eSChris LattnerThe point of going into user-defined operators in a tutorial like this 41d80f118eSChris Lattneris to show the power and flexibility of using a hand-written parser. 42d80f118eSChris LattnerThus far, the parser we have been implementing uses recursive descent 43d80f118eSChris Lattnerfor most parts of the grammar and operator precedence parsing for the 44d80f118eSChris Lattnerexpressions. See `Chapter 2 <LangImpl02.html>`_ for details. By 45d80f118eSChris Lattnerusing operator precedence parsing, it is very easy to allow 46d80f118eSChris Lattnerthe programmer to introduce new operators into the grammar: the grammar 47d80f118eSChris Lattneris dynamically extensible as the JIT runs. 48d80f118eSChris Lattner 49d80f118eSChris LattnerThe two specific features we'll add are programmable unary operators 50d80f118eSChris Lattner(right now, Kaleidoscope has no unary operators at all) as well as 51d80f118eSChris Lattnerbinary operators. An example of this is: 52d80f118eSChris Lattner 53d80f118eSChris Lattner:: 54d80f118eSChris Lattner 55d80f118eSChris Lattner # Logical unary not. 56d80f118eSChris Lattner def unary!(v) 57d80f118eSChris Lattner if v then 58d80f118eSChris Lattner 0 59d80f118eSChris Lattner else 60d80f118eSChris Lattner 1; 61d80f118eSChris Lattner 62d80f118eSChris Lattner # Define > with the same precedence as <. 63d80f118eSChris Lattner def binary> 10 (LHS RHS) 64d80f118eSChris Lattner RHS < LHS; 65d80f118eSChris Lattner 66d80f118eSChris Lattner # Binary "logical or", (note that it does not "short circuit") 67d80f118eSChris Lattner def binary| 5 (LHS RHS) 68d80f118eSChris Lattner if LHS then 69d80f118eSChris Lattner 1 70d80f118eSChris Lattner else if RHS then 71d80f118eSChris Lattner 1 72d80f118eSChris Lattner else 73d80f118eSChris Lattner 0; 74d80f118eSChris Lattner 75d80f118eSChris Lattner # Define = with slightly lower precedence than relationals. 76d80f118eSChris Lattner def binary= 9 (LHS RHS) 77d80f118eSChris Lattner !(LHS < RHS | LHS > RHS); 78d80f118eSChris Lattner 79d80f118eSChris LattnerMany languages aspire to being able to implement their standard runtime 80d80f118eSChris Lattnerlibrary in the language itself. In Kaleidoscope, we can implement 81d80f118eSChris Lattnersignificant parts of the language in the library! 82d80f118eSChris Lattner 83d80f118eSChris LattnerWe will break down implementation of these features into two parts: 84d80f118eSChris Lattnerimplementing support for user-defined binary operators and adding unary 85d80f118eSChris Lattneroperators. 86d80f118eSChris Lattner 87d80f118eSChris LattnerUser-defined Binary Operators 88d80f118eSChris Lattner============================= 89d80f118eSChris Lattner 90d80f118eSChris LattnerAdding support for user-defined binary operators is pretty simple with 91d80f118eSChris Lattnerour current framework. We'll first add support for the unary/binary 92d80f118eSChris Lattnerkeywords: 93d80f118eSChris Lattner 94d80f118eSChris Lattner.. code-block:: c++ 95d80f118eSChris Lattner 96d80f118eSChris Lattner enum Token { 97d80f118eSChris Lattner ... 98d80f118eSChris Lattner // operators 99d80f118eSChris Lattner tok_binary = -11, 100d80f118eSChris Lattner tok_unary = -12 101d80f118eSChris Lattner }; 102d80f118eSChris Lattner ... 103d80f118eSChris Lattner static int gettok() { 104d80f118eSChris Lattner ... 105d80f118eSChris Lattner if (IdentifierStr == "for") 106d80f118eSChris Lattner return tok_for; 107d80f118eSChris Lattner if (IdentifierStr == "in") 108d80f118eSChris Lattner return tok_in; 109d80f118eSChris Lattner if (IdentifierStr == "binary") 110d80f118eSChris Lattner return tok_binary; 111d80f118eSChris Lattner if (IdentifierStr == "unary") 112d80f118eSChris Lattner return tok_unary; 113d80f118eSChris Lattner return tok_identifier; 114d80f118eSChris Lattner 115d80f118eSChris LattnerThis just adds lexer support for the unary and binary keywords, like we 1166bfd10ffSJonathan Roelofsdid in `previous chapters <LangImpl05.html#lexer-extensions-for-if-then-else>`_. One nice thing 117d80f118eSChris Lattnerabout our current AST, is that we represent binary operators with full 118d80f118eSChris Lattnergeneralisation by using their ASCII code as the opcode. For our extended 119d80f118eSChris Lattneroperators, we'll use this same representation, so we don't need any new 120d80f118eSChris LattnerAST or parser support. 121d80f118eSChris Lattner 122d80f118eSChris LattnerOn the other hand, we have to be able to represent the definitions of 123d80f118eSChris Lattnerthese new operators, in the "def binary\| 5" part of the function 124d80f118eSChris Lattnerdefinition. In our grammar so far, the "name" for the function 125d80f118eSChris Lattnerdefinition is parsed as the "prototype" production and into the 126d80f118eSChris Lattner``PrototypeAST`` AST node. To represent our new user-defined operators 127d80f118eSChris Lattneras prototypes, we have to extend the ``PrototypeAST`` AST node like 128d80f118eSChris Lattnerthis: 129d80f118eSChris Lattner 130d80f118eSChris Lattner.. code-block:: c++ 131d80f118eSChris Lattner 132d80f118eSChris Lattner /// PrototypeAST - This class represents the "prototype" for a function, 133d80f118eSChris Lattner /// which captures its argument names as well as if it is an operator. 134d80f118eSChris Lattner class PrototypeAST { 135d80f118eSChris Lattner std::string Name; 136d80f118eSChris Lattner std::vector<std::string> Args; 137d80f118eSChris Lattner bool IsOperator; 138d80f118eSChris Lattner unsigned Precedence; // Precedence if a binary op. 139d80f118eSChris Lattner 140d80f118eSChris Lattner public: 141d80f118eSChris Lattner PrototypeAST(const std::string &name, std::vector<std::string> Args, 142d80f118eSChris Lattner bool IsOperator = false, unsigned Prec = 0) 143d80f118eSChris Lattner : Name(name), Args(std::move(Args)), IsOperator(IsOperator), 144d80f118eSChris Lattner Precedence(Prec) {} 145d80f118eSChris Lattner 146d80f118eSChris Lattner Function *codegen(); 147d80f118eSChris Lattner const std::string &getName() const { return Name; } 148d80f118eSChris Lattner 149d80f118eSChris Lattner bool isUnaryOp() const { return IsOperator && Args.size() == 1; } 150d80f118eSChris Lattner bool isBinaryOp() const { return IsOperator && Args.size() == 2; } 151d80f118eSChris Lattner 152d80f118eSChris Lattner char getOperatorName() const { 153d80f118eSChris Lattner assert(isUnaryOp() || isBinaryOp()); 154d80f118eSChris Lattner return Name[Name.size() - 1]; 155d80f118eSChris Lattner } 156d80f118eSChris Lattner 157d80f118eSChris Lattner unsigned getBinaryPrecedence() const { return Precedence; } 158d80f118eSChris Lattner }; 159d80f118eSChris Lattner 160d80f118eSChris LattnerBasically, in addition to knowing a name for the prototype, we now keep 161d80f118eSChris Lattnertrack of whether it was an operator, and if it was, what precedence 162d80f118eSChris Lattnerlevel the operator is at. The precedence is only used for binary 163d80f118eSChris Lattneroperators (as you'll see below, it just doesn't apply for unary 164d80f118eSChris Lattneroperators). Now that we have a way to represent the prototype for a 165d80f118eSChris Lattneruser-defined operator, we need to parse it: 166d80f118eSChris Lattner 167d80f118eSChris Lattner.. code-block:: c++ 168d80f118eSChris Lattner 169d80f118eSChris Lattner /// prototype 170d80f118eSChris Lattner /// ::= id '(' id* ')' 171d80f118eSChris Lattner /// ::= binary LETTER number? (id, id) 172d80f118eSChris Lattner static std::unique_ptr<PrototypeAST> ParsePrototype() { 173d80f118eSChris Lattner std::string FnName; 174d80f118eSChris Lattner 175d80f118eSChris Lattner unsigned Kind = 0; // 0 = identifier, 1 = unary, 2 = binary. 176d80f118eSChris Lattner unsigned BinaryPrecedence = 30; 177d80f118eSChris Lattner 178d80f118eSChris Lattner switch (CurTok) { 179d80f118eSChris Lattner default: 180d80f118eSChris Lattner return LogErrorP("Expected function name in prototype"); 181d80f118eSChris Lattner case tok_identifier: 182d80f118eSChris Lattner FnName = IdentifierStr; 183d80f118eSChris Lattner Kind = 0; 184d80f118eSChris Lattner getNextToken(); 185d80f118eSChris Lattner break; 186d80f118eSChris Lattner case tok_binary: 187d80f118eSChris Lattner getNextToken(); 188d80f118eSChris Lattner if (!isascii(CurTok)) 189d80f118eSChris Lattner return LogErrorP("Expected binary operator"); 190d80f118eSChris Lattner FnName = "binary"; 191d80f118eSChris Lattner FnName += (char)CurTok; 192d80f118eSChris Lattner Kind = 2; 193d80f118eSChris Lattner getNextToken(); 194d80f118eSChris Lattner 195d80f118eSChris Lattner // Read the precedence if present. 196d80f118eSChris Lattner if (CurTok == tok_number) { 197d80f118eSChris Lattner if (NumVal < 1 || NumVal > 100) 198d80f118eSChris Lattner return LogErrorP("Invalid precedence: must be 1..100"); 199d80f118eSChris Lattner BinaryPrecedence = (unsigned)NumVal; 200d80f118eSChris Lattner getNextToken(); 201d80f118eSChris Lattner } 202d80f118eSChris Lattner break; 203d80f118eSChris Lattner } 204d80f118eSChris Lattner 205d80f118eSChris Lattner if (CurTok != '(') 206d80f118eSChris Lattner return LogErrorP("Expected '(' in prototype"); 207d80f118eSChris Lattner 208d80f118eSChris Lattner std::vector<std::string> ArgNames; 209d80f118eSChris Lattner while (getNextToken() == tok_identifier) 210d80f118eSChris Lattner ArgNames.push_back(IdentifierStr); 211d80f118eSChris Lattner if (CurTok != ')') 212d80f118eSChris Lattner return LogErrorP("Expected ')' in prototype"); 213d80f118eSChris Lattner 214d80f118eSChris Lattner // success. 215d80f118eSChris Lattner getNextToken(); // eat ')'. 216d80f118eSChris Lattner 217d80f118eSChris Lattner // Verify right number of names for operator. 218d80f118eSChris Lattner if (Kind && ArgNames.size() != Kind) 219d80f118eSChris Lattner return LogErrorP("Invalid number of operands for operator"); 220d80f118eSChris Lattner 2210eaee545SJonas Devlieghere return std::make_unique<PrototypeAST>(FnName, std::move(ArgNames), Kind != 0, 222d80f118eSChris Lattner BinaryPrecedence); 223d80f118eSChris Lattner } 224d80f118eSChris Lattner 225d80f118eSChris LattnerThis is all fairly straightforward parsing code, and we have already 226d80f118eSChris Lattnerseen a lot of similar code in the past. One interesting part about the 227d80f118eSChris Lattnercode above is the couple lines that set up ``FnName`` for binary 228d80f118eSChris Lattneroperators. This builds names like "binary@" for a newly defined "@" 229d80f118eSChris Lattneroperator. It then takes advantage of the fact that symbol names in the 230d80f118eSChris LattnerLLVM symbol table are allowed to have any character in them, including 231d80f118eSChris Lattnerembedded nul characters. 232d80f118eSChris Lattner 233d80f118eSChris LattnerThe next interesting thing to add, is codegen support for these binary 234d80f118eSChris Lattneroperators. Given our current structure, this is a simple addition of a 235d80f118eSChris Lattnerdefault case for our existing binary operator node: 236d80f118eSChris Lattner 237d80f118eSChris Lattner.. code-block:: c++ 238d80f118eSChris Lattner 239d80f118eSChris Lattner Value *BinaryExprAST::codegen() { 240d80f118eSChris Lattner Value *L = LHS->codegen(); 241d80f118eSChris Lattner Value *R = RHS->codegen(); 242d80f118eSChris Lattner if (!L || !R) 243d80f118eSChris Lattner return nullptr; 244d80f118eSChris Lattner 245d80f118eSChris Lattner switch (Op) { 246d80f118eSChris Lattner case '+': 247d80f118eSChris Lattner return Builder.CreateFAdd(L, R, "addtmp"); 248d80f118eSChris Lattner case '-': 249d80f118eSChris Lattner return Builder.CreateFSub(L, R, "subtmp"); 250d80f118eSChris Lattner case '*': 251d80f118eSChris Lattner return Builder.CreateFMul(L, R, "multmp"); 252d80f118eSChris Lattner case '<': 253d80f118eSChris Lattner L = Builder.CreateFCmpULT(L, R, "cmptmp"); 254d80f118eSChris Lattner // Convert bool 0/1 to double 0.0 or 1.0 255d80f118eSChris Lattner return Builder.CreateUIToFP(L, Type::getDoubleTy(TheContext), 256d80f118eSChris Lattner "booltmp"); 257d80f118eSChris Lattner default: 258d80f118eSChris Lattner break; 259d80f118eSChris Lattner } 260d80f118eSChris Lattner 261d80f118eSChris Lattner // If it wasn't a builtin binary operator, it must be a user defined one. Emit 262d80f118eSChris Lattner // a call to it. 263d80f118eSChris Lattner Function *F = getFunction(std::string("binary") + Op); 264d80f118eSChris Lattner assert(F && "binary operator not found!"); 265d80f118eSChris Lattner 266d80f118eSChris Lattner Value *Ops[2] = { L, R }; 267d80f118eSChris Lattner return Builder.CreateCall(F, Ops, "binop"); 268d80f118eSChris Lattner } 269d80f118eSChris Lattner 270d80f118eSChris LattnerAs you can see above, the new code is actually really simple. It just 271d80f118eSChris Lattnerdoes a lookup for the appropriate operator in the symbol table and 272d80f118eSChris Lattnergenerates a function call to it. Since user-defined operators are just 273d80f118eSChris Lattnerbuilt as normal functions (because the "prototype" boils down to a 274d80f118eSChris Lattnerfunction with the right name) everything falls into place. 275d80f118eSChris Lattner 276d80f118eSChris LattnerThe final piece of code we are missing, is a bit of top-level magic: 277d80f118eSChris Lattner 278d80f118eSChris Lattner.. code-block:: c++ 279d80f118eSChris Lattner 280d80f118eSChris Lattner Function *FunctionAST::codegen() { 281d80f118eSChris Lattner // Transfer ownership of the prototype to the FunctionProtos map, but keep a 282d80f118eSChris Lattner // reference to it for use below. 283d80f118eSChris Lattner auto &P = *Proto; 284d80f118eSChris Lattner FunctionProtos[Proto->getName()] = std::move(Proto); 285d80f118eSChris Lattner Function *TheFunction = getFunction(P.getName()); 286d80f118eSChris Lattner if (!TheFunction) 287d80f118eSChris Lattner return nullptr; 288d80f118eSChris Lattner 289d80f118eSChris Lattner // If this is an operator, install it. 290d80f118eSChris Lattner if (P.isBinaryOp()) 291d80f118eSChris Lattner BinopPrecedence[P.getOperatorName()] = P.getBinaryPrecedence(); 292d80f118eSChris Lattner 293d80f118eSChris Lattner // Create a new basic block to start insertion into. 294d80f118eSChris Lattner BasicBlock *BB = BasicBlock::Create(TheContext, "entry", TheFunction); 295d80f118eSChris Lattner ... 296d80f118eSChris Lattner 297d80f118eSChris LattnerBasically, before codegening a function, if it is a user-defined 298d80f118eSChris Lattneroperator, we register it in the precedence table. This allows the binary 299d80f118eSChris Lattneroperator parsing logic we already have in place to handle it. Since we 300d80f118eSChris Lattnerare working on a fully-general operator precedence parser, this is all 301d80f118eSChris Lattnerwe need to do to "extend the grammar". 302d80f118eSChris Lattner 303d80f118eSChris LattnerNow we have useful user-defined binary operators. This builds a lot on 304d80f118eSChris Lattnerthe previous framework we built for other operators. Adding unary 305d80f118eSChris Lattneroperators is a bit more challenging, because we don't have any framework 306d80f118eSChris Lattnerfor it yet - let's see what it takes. 307d80f118eSChris Lattner 308d80f118eSChris LattnerUser-defined Unary Operators 309d80f118eSChris Lattner============================ 310d80f118eSChris Lattner 311d80f118eSChris LattnerSince we don't currently support unary operators in the Kaleidoscope 312d80f118eSChris Lattnerlanguage, we'll need to add everything to support them. Above, we added 313d80f118eSChris Lattnersimple support for the 'unary' keyword to the lexer. In addition to 314d80f118eSChris Lattnerthat, we need an AST node: 315d80f118eSChris Lattner 316d80f118eSChris Lattner.. code-block:: c++ 317d80f118eSChris Lattner 318d80f118eSChris Lattner /// UnaryExprAST - Expression class for a unary operator. 319d80f118eSChris Lattner class UnaryExprAST : public ExprAST { 320d80f118eSChris Lattner char Opcode; 321d80f118eSChris Lattner std::unique_ptr<ExprAST> Operand; 322d80f118eSChris Lattner 323d80f118eSChris Lattner public: 324d80f118eSChris Lattner UnaryExprAST(char Opcode, std::unique_ptr<ExprAST> Operand) 325d80f118eSChris Lattner : Opcode(Opcode), Operand(std::move(Operand)) {} 326d80f118eSChris Lattner 327d80f118eSChris Lattner Value *codegen() override; 328d80f118eSChris Lattner }; 329d80f118eSChris Lattner 330d80f118eSChris LattnerThis AST node is very simple and obvious by now. It directly mirrors the 331d80f118eSChris Lattnerbinary operator AST node, except that it only has one child. With this, 332d80f118eSChris Lattnerwe need to add the parsing logic. Parsing a unary operator is pretty 333d80f118eSChris Lattnersimple: we'll add a new function to do it: 334d80f118eSChris Lattner 335d80f118eSChris Lattner.. code-block:: c++ 336d80f118eSChris Lattner 337d80f118eSChris Lattner /// unary 338d80f118eSChris Lattner /// ::= primary 339d80f118eSChris Lattner /// ::= '!' unary 340d80f118eSChris Lattner static std::unique_ptr<ExprAST> ParseUnary() { 341d80f118eSChris Lattner // If the current token is not an operator, it must be a primary expr. 342d80f118eSChris Lattner if (!isascii(CurTok) || CurTok == '(' || CurTok == ',') 343d80f118eSChris Lattner return ParsePrimary(); 344d80f118eSChris Lattner 345d80f118eSChris Lattner // If this is a unary operator, read it. 346d80f118eSChris Lattner int Opc = CurTok; 347d80f118eSChris Lattner getNextToken(); 348d80f118eSChris Lattner if (auto Operand = ParseUnary()) 3490eaee545SJonas Devlieghere return std::make_unique<UnaryExprAST>(Opc, std::move(Operand)); 350d80f118eSChris Lattner return nullptr; 351d80f118eSChris Lattner } 352d80f118eSChris Lattner 353d80f118eSChris LattnerThe grammar we add is pretty straightforward here. If we see a unary 354d80f118eSChris Lattneroperator when parsing a primary operator, we eat the operator as a 355d80f118eSChris Lattnerprefix and parse the remaining piece as another unary operator. This 356d80f118eSChris Lattnerallows us to handle multiple unary operators (e.g. "!!x"). Note that 357d80f118eSChris Lattnerunary operators can't have ambiguous parses like binary operators can, 358d80f118eSChris Lattnerso there is no need for precedence information. 359d80f118eSChris Lattner 360d80f118eSChris LattnerThe problem with this function, is that we need to call ParseUnary from 361d80f118eSChris Lattnersomewhere. To do this, we change previous callers of ParsePrimary to 362d80f118eSChris Lattnercall ParseUnary instead: 363d80f118eSChris Lattner 364d80f118eSChris Lattner.. code-block:: c++ 365d80f118eSChris Lattner 366d80f118eSChris Lattner /// binoprhs 367d80f118eSChris Lattner /// ::= ('+' unary)* 368d80f118eSChris Lattner static std::unique_ptr<ExprAST> ParseBinOpRHS(int ExprPrec, 369d80f118eSChris Lattner std::unique_ptr<ExprAST> LHS) { 370d80f118eSChris Lattner ... 371d80f118eSChris Lattner // Parse the unary expression after the binary operator. 372d80f118eSChris Lattner auto RHS = ParseUnary(); 373d80f118eSChris Lattner if (!RHS) 374d80f118eSChris Lattner return nullptr; 375d80f118eSChris Lattner ... 376d80f118eSChris Lattner } 377d80f118eSChris Lattner /// expression 378d80f118eSChris Lattner /// ::= unary binoprhs 379d80f118eSChris Lattner /// 380d80f118eSChris Lattner static std::unique_ptr<ExprAST> ParseExpression() { 381d80f118eSChris Lattner auto LHS = ParseUnary(); 382d80f118eSChris Lattner if (!LHS) 383d80f118eSChris Lattner return nullptr; 384d80f118eSChris Lattner 385d80f118eSChris Lattner return ParseBinOpRHS(0, std::move(LHS)); 386d80f118eSChris Lattner } 387d80f118eSChris Lattner 388d80f118eSChris LattnerWith these two simple changes, we are now able to parse unary operators 389d80f118eSChris Lattnerand build the AST for them. Next up, we need to add parser support for 390d80f118eSChris Lattnerprototypes, to parse the unary operator prototype. We extend the binary 391d80f118eSChris Lattneroperator code above with: 392d80f118eSChris Lattner 393d80f118eSChris Lattner.. code-block:: c++ 394d80f118eSChris Lattner 395d80f118eSChris Lattner /// prototype 396d80f118eSChris Lattner /// ::= id '(' id* ')' 397d80f118eSChris Lattner /// ::= binary LETTER number? (id, id) 398d80f118eSChris Lattner /// ::= unary LETTER (id) 399d80f118eSChris Lattner static std::unique_ptr<PrototypeAST> ParsePrototype() { 400d80f118eSChris Lattner std::string FnName; 401d80f118eSChris Lattner 402d80f118eSChris Lattner unsigned Kind = 0; // 0 = identifier, 1 = unary, 2 = binary. 403d80f118eSChris Lattner unsigned BinaryPrecedence = 30; 404d80f118eSChris Lattner 405d80f118eSChris Lattner switch (CurTok) { 406d80f118eSChris Lattner default: 407d80f118eSChris Lattner return LogErrorP("Expected function name in prototype"); 408d80f118eSChris Lattner case tok_identifier: 409d80f118eSChris Lattner FnName = IdentifierStr; 410d80f118eSChris Lattner Kind = 0; 411d80f118eSChris Lattner getNextToken(); 412d80f118eSChris Lattner break; 413d80f118eSChris Lattner case tok_unary: 414d80f118eSChris Lattner getNextToken(); 415d80f118eSChris Lattner if (!isascii(CurTok)) 416d80f118eSChris Lattner return LogErrorP("Expected unary operator"); 417d80f118eSChris Lattner FnName = "unary"; 418d80f118eSChris Lattner FnName += (char)CurTok; 419d80f118eSChris Lattner Kind = 1; 420d80f118eSChris Lattner getNextToken(); 421d80f118eSChris Lattner break; 422d80f118eSChris Lattner case tok_binary: 423d80f118eSChris Lattner ... 424d80f118eSChris Lattner 425d80f118eSChris LattnerAs with binary operators, we name unary operators with a name that 426d80f118eSChris Lattnerincludes the operator character. This assists us at code generation 427d80f118eSChris Lattnertime. Speaking of, the final piece we need to add is codegen support for 428d80f118eSChris Lattnerunary operators. It looks like this: 429d80f118eSChris Lattner 430d80f118eSChris Lattner.. code-block:: c++ 431d80f118eSChris Lattner 432d80f118eSChris Lattner Value *UnaryExprAST::codegen() { 433d80f118eSChris Lattner Value *OperandV = Operand->codegen(); 434d80f118eSChris Lattner if (!OperandV) 435d80f118eSChris Lattner return nullptr; 436d80f118eSChris Lattner 437d80f118eSChris Lattner Function *F = getFunction(std::string("unary") + Opcode); 438d80f118eSChris Lattner if (!F) 439d80f118eSChris Lattner return LogErrorV("Unknown unary operator"); 440d80f118eSChris Lattner 441d80f118eSChris Lattner return Builder.CreateCall(F, OperandV, "unop"); 442d80f118eSChris Lattner } 443d80f118eSChris Lattner 444d80f118eSChris LattnerThis code is similar to, but simpler than, the code for binary 445d80f118eSChris Lattneroperators. It is simpler primarily because it doesn't need to handle any 446d80f118eSChris Lattnerpredefined operators. 447d80f118eSChris Lattner 448d80f118eSChris LattnerKicking the Tires 449d80f118eSChris Lattner================= 450d80f118eSChris Lattner 451d80f118eSChris LattnerIt is somewhat hard to believe, but with a few simple extensions we've 452d80f118eSChris Lattnercovered in the last chapters, we have grown a real-ish language. With 453d80f118eSChris Lattnerthis, we can do a lot of interesting things, including I/O, math, and a 454d80f118eSChris Lattnerbunch of other things. For example, we can now add a nice sequencing 455d80f118eSChris Lattneroperator (printd is defined to print out the specified value and a 456d80f118eSChris Lattnernewline): 457d80f118eSChris Lattner 458d80f118eSChris Lattner:: 459d80f118eSChris Lattner 460d80f118eSChris Lattner ready> extern printd(x); 461d80f118eSChris Lattner Read extern: 462d80f118eSChris Lattner declare double @printd(double) 463d80f118eSChris Lattner 464d80f118eSChris Lattner ready> def binary : 1 (x y) 0; # Low-precedence operator that ignores operands. 465d80f118eSChris Lattner ... 466d80f118eSChris Lattner ready> printd(123) : printd(456) : printd(789); 467d80f118eSChris Lattner 123.000000 468d80f118eSChris Lattner 456.000000 469d80f118eSChris Lattner 789.000000 470d80f118eSChris Lattner Evaluated to 0.000000 471d80f118eSChris Lattner 472d80f118eSChris LattnerWe can also define a bunch of other "primitive" operations, such as: 473d80f118eSChris Lattner 474d80f118eSChris Lattner:: 475d80f118eSChris Lattner 476d80f118eSChris Lattner # Logical unary not. 477d80f118eSChris Lattner def unary!(v) 478d80f118eSChris Lattner if v then 479d80f118eSChris Lattner 0 480d80f118eSChris Lattner else 481d80f118eSChris Lattner 1; 482d80f118eSChris Lattner 483d80f118eSChris Lattner # Unary negate. 484d80f118eSChris Lattner def unary-(v) 485d80f118eSChris Lattner 0-v; 486d80f118eSChris Lattner 487d80f118eSChris Lattner # Define > with the same precedence as <. 488d80f118eSChris Lattner def binary> 10 (LHS RHS) 489d80f118eSChris Lattner RHS < LHS; 490d80f118eSChris Lattner 491d80f118eSChris Lattner # Binary logical or, which does not short circuit. 492d80f118eSChris Lattner def binary| 5 (LHS RHS) 493d80f118eSChris Lattner if LHS then 494d80f118eSChris Lattner 1 495d80f118eSChris Lattner else if RHS then 496d80f118eSChris Lattner 1 497d80f118eSChris Lattner else 498d80f118eSChris Lattner 0; 499d80f118eSChris Lattner 500d80f118eSChris Lattner # Binary logical and, which does not short circuit. 501d80f118eSChris Lattner def binary& 6 (LHS RHS) 502d80f118eSChris Lattner if !LHS then 503d80f118eSChris Lattner 0 504d80f118eSChris Lattner else 505d80f118eSChris Lattner !!RHS; 506d80f118eSChris Lattner 507d80f118eSChris Lattner # Define = with slightly lower precedence than relationals. 508d80f118eSChris Lattner def binary = 9 (LHS RHS) 509d80f118eSChris Lattner !(LHS < RHS | LHS > RHS); 510d80f118eSChris Lattner 511d80f118eSChris Lattner # Define ':' for sequencing: as a low-precedence operator that ignores operands 512d80f118eSChris Lattner # and just returns the RHS. 513d80f118eSChris Lattner def binary : 1 (x y) y; 514d80f118eSChris Lattner 515d80f118eSChris LattnerGiven the previous if/then/else support, we can also define interesting 516d80f118eSChris Lattnerfunctions for I/O. For example, the following prints out a character 517d80f118eSChris Lattnerwhose "density" reflects the value passed in: the lower the value, the 518d80f118eSChris Lattnerdenser the character: 519d80f118eSChris Lattner 520d80f118eSChris Lattner:: 521d80f118eSChris Lattner 522d80f118eSChris Lattner ready> extern putchard(char); 523d80f118eSChris Lattner ... 524d80f118eSChris Lattner ready> def printdensity(d) 525d80f118eSChris Lattner if d > 8 then 526d80f118eSChris Lattner putchard(32) # ' ' 527d80f118eSChris Lattner else if d > 4 then 528d80f118eSChris Lattner putchard(46) # '.' 529d80f118eSChris Lattner else if d > 2 then 530d80f118eSChris Lattner putchard(43) # '+' 531d80f118eSChris Lattner else 532d80f118eSChris Lattner putchard(42); # '*' 533d80f118eSChris Lattner ... 534d80f118eSChris Lattner ready> printdensity(1): printdensity(2): printdensity(3): 535d80f118eSChris Lattner printdensity(4): printdensity(5): printdensity(9): 536d80f118eSChris Lattner putchard(10); 537d80f118eSChris Lattner **++. 538d80f118eSChris Lattner Evaluated to 0.000000 539d80f118eSChris Lattner 540d80f118eSChris LattnerBased on these simple primitive operations, we can start to define more 541d80f118eSChris Lattnerinteresting things. For example, here's a little function that determines 542d80f118eSChris Lattnerthe number of iterations it takes for a certain function in the complex 543d80f118eSChris Lattnerplane to diverge: 544d80f118eSChris Lattner 545d80f118eSChris Lattner:: 546d80f118eSChris Lattner 547d80f118eSChris Lattner # Determine whether the specific location diverges. 548d80f118eSChris Lattner # Solve for z = z^2 + c in the complex plane. 549d80f118eSChris Lattner def mandelconverger(real imag iters creal cimag) 550d80f118eSChris Lattner if iters > 255 | (real*real + imag*imag > 4) then 551d80f118eSChris Lattner iters 552d80f118eSChris Lattner else 553d80f118eSChris Lattner mandelconverger(real*real - imag*imag + creal, 554d80f118eSChris Lattner 2*real*imag + cimag, 555d80f118eSChris Lattner iters+1, creal, cimag); 556d80f118eSChris Lattner 557d80f118eSChris Lattner # Return the number of iterations required for the iteration to escape 558d80f118eSChris Lattner def mandelconverge(real imag) 559d80f118eSChris Lattner mandelconverger(real, imag, 0, real, imag); 560d80f118eSChris Lattner 561d80f118eSChris LattnerThis "``z = z2 + c``" function is a beautiful little creature that is 562d80f118eSChris Lattnerthe basis for computation of the `Mandelbrot 563d80f118eSChris LattnerSet <http://en.wikipedia.org/wiki/Mandelbrot_set>`_. Our 564d80f118eSChris Lattner``mandelconverge`` function returns the number of iterations that it 565d80f118eSChris Lattnertakes for a complex orbit to escape, saturating to 255. This is not a 566d80f118eSChris Lattnervery useful function by itself, but if you plot its value over a 567d80f118eSChris Lattnertwo-dimensional plane, you can see the Mandelbrot set. Given that we are 568d80f118eSChris Lattnerlimited to using putchard here, our amazing graphical output is limited, 569d80f118eSChris Lattnerbut we can whip together something using the density plotter above: 570d80f118eSChris Lattner 571d80f118eSChris Lattner:: 572d80f118eSChris Lattner 573d80f118eSChris Lattner # Compute and plot the mandelbrot set with the specified 2 dimensional range 574d80f118eSChris Lattner # info. 575d80f118eSChris Lattner def mandelhelp(xmin xmax xstep ymin ymax ystep) 576d80f118eSChris Lattner for y = ymin, y < ymax, ystep in ( 577d80f118eSChris Lattner (for x = xmin, x < xmax, xstep in 578d80f118eSChris Lattner printdensity(mandelconverge(x,y))) 579d80f118eSChris Lattner : putchard(10) 580d80f118eSChris Lattner ) 581d80f118eSChris Lattner 582d80f118eSChris Lattner # mandel - This is a convenient helper function for plotting the mandelbrot set 583d80f118eSChris Lattner # from the specified position with the specified Magnification. 584d80f118eSChris Lattner def mandel(realstart imagstart realmag imagmag) 585d80f118eSChris Lattner mandelhelp(realstart, realstart+realmag*78, realmag, 586d80f118eSChris Lattner imagstart, imagstart+imagmag*40, imagmag); 587d80f118eSChris Lattner 588d80f118eSChris LattnerGiven this, we can try plotting out the mandelbrot set! Lets try it out: 589d80f118eSChris Lattner 590d80f118eSChris Lattner:: 591d80f118eSChris Lattner 592d80f118eSChris Lattner ready> mandel(-2.3, -1.3, 0.05, 0.07); 593d80f118eSChris Lattner *******************************+++++++++++************************************* 594d80f118eSChris Lattner *************************+++++++++++++++++++++++******************************* 595d80f118eSChris Lattner **********************+++++++++++++++++++++++++++++**************************** 596d80f118eSChris Lattner *******************+++++++++++++++++++++.. ...++++++++************************* 597d80f118eSChris Lattner *****************++++++++++++++++++++++.... ...+++++++++*********************** 598d80f118eSChris Lattner ***************+++++++++++++++++++++++..... ...+++++++++********************* 599d80f118eSChris Lattner **************+++++++++++++++++++++++.... ....+++++++++******************** 600d80f118eSChris Lattner *************++++++++++++++++++++++...... .....++++++++******************* 601d80f118eSChris Lattner ************+++++++++++++++++++++....... .......+++++++****************** 602d80f118eSChris Lattner ***********+++++++++++++++++++.... ... .+++++++***************** 603d80f118eSChris Lattner **********+++++++++++++++++....... .+++++++**************** 604d80f118eSChris Lattner *********++++++++++++++........... ...+++++++*************** 605d80f118eSChris Lattner ********++++++++++++............ ...++++++++************** 606d80f118eSChris Lattner ********++++++++++... .......... .++++++++************** 607d80f118eSChris Lattner *******+++++++++..... .+++++++++************* 608d80f118eSChris Lattner *******++++++++...... ..+++++++++************* 609d80f118eSChris Lattner *******++++++....... ..+++++++++************* 610d80f118eSChris Lattner *******+++++...... ..+++++++++************* 611d80f118eSChris Lattner *******.... .... ...+++++++++************* 612d80f118eSChris Lattner *******.... . ...+++++++++************* 613d80f118eSChris Lattner *******+++++...... ...+++++++++************* 614d80f118eSChris Lattner *******++++++....... ..+++++++++************* 615d80f118eSChris Lattner *******++++++++...... .+++++++++************* 616d80f118eSChris Lattner *******+++++++++..... ..+++++++++************* 617d80f118eSChris Lattner ********++++++++++... .......... .++++++++************** 618d80f118eSChris Lattner ********++++++++++++............ ...++++++++************** 619d80f118eSChris Lattner *********++++++++++++++.......... ...+++++++*************** 620d80f118eSChris Lattner **********++++++++++++++++........ .+++++++**************** 621d80f118eSChris Lattner **********++++++++++++++++++++.... ... ..+++++++**************** 622d80f118eSChris Lattner ***********++++++++++++++++++++++....... .......++++++++***************** 623d80f118eSChris Lattner ************+++++++++++++++++++++++...... ......++++++++****************** 624d80f118eSChris Lattner **************+++++++++++++++++++++++.... ....++++++++******************** 625d80f118eSChris Lattner ***************+++++++++++++++++++++++..... ...+++++++++********************* 626d80f118eSChris Lattner *****************++++++++++++++++++++++.... ...++++++++*********************** 627d80f118eSChris Lattner *******************+++++++++++++++++++++......++++++++************************* 628d80f118eSChris Lattner *********************++++++++++++++++++++++.++++++++*************************** 629d80f118eSChris Lattner *************************+++++++++++++++++++++++******************************* 630d80f118eSChris Lattner ******************************+++++++++++++************************************ 631d80f118eSChris Lattner ******************************************************************************* 632d80f118eSChris Lattner ******************************************************************************* 633d80f118eSChris Lattner ******************************************************************************* 634d80f118eSChris Lattner Evaluated to 0.000000 635d80f118eSChris Lattner ready> mandel(-2, -1, 0.02, 0.04); 636d80f118eSChris Lattner **************************+++++++++++++++++++++++++++++++++++++++++++++++++++++ 637d80f118eSChris Lattner ***********************++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 638d80f118eSChris Lattner *********************+++++++++++++++++++++++++++++++++++++++++++++++++++++++++. 639d80f118eSChris Lattner *******************+++++++++++++++++++++++++++++++++++++++++++++++++++++++++... 640d80f118eSChris Lattner *****************+++++++++++++++++++++++++++++++++++++++++++++++++++++++++..... 641d80f118eSChris Lattner ***************++++++++++++++++++++++++++++++++++++++++++++++++++++++++........ 642d80f118eSChris Lattner **************++++++++++++++++++++++++++++++++++++++++++++++++++++++........... 643d80f118eSChris Lattner ************+++++++++++++++++++++++++++++++++++++++++++++++++++++.............. 644d80f118eSChris Lattner ***********++++++++++++++++++++++++++++++++++++++++++++++++++........ . 645d80f118eSChris Lattner **********++++++++++++++++++++++++++++++++++++++++++++++............. 646d80f118eSChris Lattner ********+++++++++++++++++++++++++++++++++++++++++++.................. 647d80f118eSChris Lattner *******+++++++++++++++++++++++++++++++++++++++....................... 648d80f118eSChris Lattner ******+++++++++++++++++++++++++++++++++++........................... 649d80f118eSChris Lattner *****++++++++++++++++++++++++++++++++............................ 650d80f118eSChris Lattner *****++++++++++++++++++++++++++++............................... 651d80f118eSChris Lattner ****++++++++++++++++++++++++++...... ......................... 652d80f118eSChris Lattner ***++++++++++++++++++++++++......... ...... ........... 653d80f118eSChris Lattner ***++++++++++++++++++++++............ 654d80f118eSChris Lattner **+++++++++++++++++++++.............. 655d80f118eSChris Lattner **+++++++++++++++++++................ 656d80f118eSChris Lattner *++++++++++++++++++................. 657d80f118eSChris Lattner *++++++++++++++++............ ... 658d80f118eSChris Lattner *++++++++++++++.............. 659d80f118eSChris Lattner *+++....++++................ 660d80f118eSChris Lattner *.......... ........... 661d80f118eSChris Lattner * 662d80f118eSChris Lattner *.......... ........... 663d80f118eSChris Lattner *+++....++++................ 664d80f118eSChris Lattner *++++++++++++++.............. 665d80f118eSChris Lattner *++++++++++++++++............ ... 666d80f118eSChris Lattner *++++++++++++++++++................. 667d80f118eSChris Lattner **+++++++++++++++++++................ 668d80f118eSChris Lattner **+++++++++++++++++++++.............. 669d80f118eSChris Lattner ***++++++++++++++++++++++............ 670d80f118eSChris Lattner ***++++++++++++++++++++++++......... ...... ........... 671d80f118eSChris Lattner ****++++++++++++++++++++++++++...... ......................... 672d80f118eSChris Lattner *****++++++++++++++++++++++++++++............................... 673d80f118eSChris Lattner *****++++++++++++++++++++++++++++++++............................ 674d80f118eSChris Lattner ******+++++++++++++++++++++++++++++++++++........................... 675d80f118eSChris Lattner *******+++++++++++++++++++++++++++++++++++++++....................... 676d80f118eSChris Lattner ********+++++++++++++++++++++++++++++++++++++++++++.................. 677d80f118eSChris Lattner Evaluated to 0.000000 678d80f118eSChris Lattner ready> mandel(-0.9, -1.4, 0.02, 0.03); 679d80f118eSChris Lattner ******************************************************************************* 680d80f118eSChris Lattner ******************************************************************************* 681d80f118eSChris Lattner ******************************************************************************* 682d80f118eSChris Lattner **********+++++++++++++++++++++************************************************ 683d80f118eSChris Lattner *+++++++++++++++++++++++++++++++++++++++*************************************** 684d80f118eSChris Lattner +++++++++++++++++++++++++++++++++++++++++++++********************************** 685d80f118eSChris Lattner ++++++++++++++++++++++++++++++++++++++++++++++++++***************************** 686d80f118eSChris Lattner ++++++++++++++++++++++++++++++++++++++++++++++++++++++************************* 687d80f118eSChris Lattner +++++++++++++++++++++++++++++++++++++++++++++++++++++++++********************** 688d80f118eSChris Lattner +++++++++++++++++++++++++++++++++.........++++++++++++++++++******************* 689d80f118eSChris Lattner +++++++++++++++++++++++++++++++.... ......+++++++++++++++++++**************** 690d80f118eSChris Lattner +++++++++++++++++++++++++++++....... ........+++++++++++++++++++************** 691d80f118eSChris Lattner ++++++++++++++++++++++++++++........ ........++++++++++++++++++++************ 692d80f118eSChris Lattner +++++++++++++++++++++++++++......... .. ...+++++++++++++++++++++********** 693d80f118eSChris Lattner ++++++++++++++++++++++++++........... ....++++++++++++++++++++++******** 694d80f118eSChris Lattner ++++++++++++++++++++++++............. .......++++++++++++++++++++++****** 695d80f118eSChris Lattner +++++++++++++++++++++++............. ........+++++++++++++++++++++++**** 696d80f118eSChris Lattner ++++++++++++++++++++++........... ..........++++++++++++++++++++++*** 697d80f118eSChris Lattner ++++++++++++++++++++........... .........++++++++++++++++++++++* 698d80f118eSChris Lattner ++++++++++++++++++............ ...........++++++++++++++++++++ 699d80f118eSChris Lattner ++++++++++++++++............... .............++++++++++++++++++ 700d80f118eSChris Lattner ++++++++++++++................. ...............++++++++++++++++ 701d80f118eSChris Lattner ++++++++++++.................. .................++++++++++++++ 702d80f118eSChris Lattner +++++++++.................. .................+++++++++++++ 703d80f118eSChris Lattner ++++++........ . ......... ..++++++++++++ 704d80f118eSChris Lattner ++............ ...... ....++++++++++ 705d80f118eSChris Lattner .............. ...++++++++++ 706d80f118eSChris Lattner .............. ....+++++++++ 707d80f118eSChris Lattner .............. .....++++++++ 708d80f118eSChris Lattner ............. ......++++++++ 709d80f118eSChris Lattner ........... .......++++++++ 710d80f118eSChris Lattner ......... ........+++++++ 711d80f118eSChris Lattner ......... ........+++++++ 712d80f118eSChris Lattner ......... ....+++++++ 713d80f118eSChris Lattner ........ ...+++++++ 714d80f118eSChris Lattner ....... ...+++++++ 715d80f118eSChris Lattner ....+++++++ 716d80f118eSChris Lattner .....+++++++ 717d80f118eSChris Lattner ....+++++++ 718d80f118eSChris Lattner ....+++++++ 719d80f118eSChris Lattner ....+++++++ 720d80f118eSChris Lattner Evaluated to 0.000000 721d80f118eSChris Lattner ready> ^D 722d80f118eSChris Lattner 723d80f118eSChris LattnerAt this point, you may be starting to realize that Kaleidoscope is a 724d80f118eSChris Lattnerreal and powerful language. It may not be self-similar :), but it can be 725d80f118eSChris Lattnerused to plot things that are! 726d80f118eSChris Lattner 727d80f118eSChris LattnerWith this, we conclude the "adding user-defined operators" chapter of 728d80f118eSChris Lattnerthe tutorial. We have successfully augmented our language, adding the 729d80f118eSChris Lattnerability to extend the language in the library, and we have shown how 730d80f118eSChris Lattnerthis can be used to build a simple but interesting end-user application 731d80f118eSChris Lattnerin Kaleidoscope. At this point, Kaleidoscope can build a variety of 732d80f118eSChris Lattnerapplications that are functional and can call functions with 733d80f118eSChris Lattnerside-effects, but it can't actually define and mutate a variable itself. 734d80f118eSChris Lattner 735d80f118eSChris LattnerStrikingly, variable mutation is an important feature of some languages, 736d80f118eSChris Lattnerand it is not at all obvious how to `add support for mutable 737d80f118eSChris Lattnervariables <LangImpl07.html>`_ without having to add an "SSA construction" 738d80f118eSChris Lattnerphase to your front-end. In the next chapter, we will describe how you 739d80f118eSChris Lattnercan add variable mutation without building SSA in your front-end. 740d80f118eSChris Lattner 741d80f118eSChris LattnerFull Code Listing 742d80f118eSChris Lattner================= 743d80f118eSChris Lattner 744d80f118eSChris LattnerHere is the complete code listing for our running example, enhanced with 745d80f118eSChris Lattnerthe support for user-defined operators. To build this example, use: 746d80f118eSChris Lattner 747d80f118eSChris Lattner.. code-block:: bash 748d80f118eSChris Lattner 749d80f118eSChris Lattner # Compile 750*3546b372Sxgupta clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orcjit native` -O3 -o toy 751d80f118eSChris Lattner # Run 752d80f118eSChris Lattner ./toy 753d80f118eSChris Lattner 754d80f118eSChris LattnerOn some platforms, you will need to specify -rdynamic or 755d80f118eSChris Lattner-Wl,--export-dynamic when linking. This ensures that symbols defined in 756d80f118eSChris Lattnerthe main executable are exported to the dynamic linker and so are 757d80f118eSChris Lattneravailable for symbol resolution at run time. This is not needed if you 758d80f118eSChris Lattnercompile your support code into a shared library, although doing that 759d80f118eSChris Lattnerwill cause problems on Windows. 760d80f118eSChris Lattner 761d80f118eSChris LattnerHere is the code: 762d80f118eSChris Lattner 763147e0ddaSHans Wennborg.. literalinclude:: ../../../examples/Kaleidoscope/Chapter6/toy.cpp 764d80f118eSChris Lattner :language: c++ 765d80f118eSChris Lattner 766d80f118eSChris Lattner`Next: Extending the language: mutable variables / SSA 767d80f118eSChris Lattnerconstruction <LangImpl07.html>`_ 768d80f118eSChris Lattner 769