Welcome to Chapter 7 of the "Implementing a language with LLVM" tutorial. In chapters 1 through 6, we've built a very respectable, albeit simple, functional programming language. In our journey, we learned some parsing techniques, how to build and represent an AST, how to build LLVM IR, and how to optimize the resultant code as well as JIT compile it.
While Kaleidoscope is interesting as a functional language, the fact that it is functional makes it "too easy" to generate LLVM IR for it. In particular, a functional language makes it very easy to build LLVM IR directly in SSA form. Since LLVM requires that the input code be in SSA form, this is a very nice property and it is often unclear to newcomers how to generate code for an imperative language with mutable variables.
The short (and happy) summary of this chapter is that there is no need for your front-end to build SSA form: LLVM provides highly tuned and well tested support for this, though the way it works is a bit unexpected for some.
To understand why mutable variables cause complexities in SSA construction, consider this extremely simple C example:
int G, H; int test(_Bool Condition) { int X; if (Condition) X = G; else X = H; return X; }
In this case, we have the variable "X", whose value depends on the path executed in the program. Because there are two different possible values for X before the return instruction, a PHI node is inserted to merge the two values. The LLVM IR that we want for this example looks like this:
@G = weak global i32 0 ; type of @G is i32* @H = weak global i32 0 ; type of @H is i32* define i32 @test(i1 %Condition) { entry: br i1 %Condition, label %cond_true, label %cond_false cond_true: %X.0 = load i32* @G br label %cond_next cond_false: %X.1 = load i32* @H br label %cond_next cond_next: %X.2 = phi i32 [ %X.1, %cond_false ], [ %X.0, %cond_true ] ret i32 %X.2 }
In this example, the loads from the G and H global variables are explicit in the LLVM IR, and they live in the then/else branches of the if statement (cond_true/cond_false). In order to merge the incoming values, the X.2 phi node in the cond_next block selects the right value to use based on where control flow is coming from: if control flow comes from the cond_false block, X.2 gets the value of X.1. Alternatively, if control flow comes from cond_true, it gets the value of X.0. The intent of this chapter is not to explain the details of SSA form. For more information, see one of the many online references.
The question for this article is "who places the phi nodes when lowering assignments to mutable variables?". The issue here is that LLVM requires that its IR be in SSA form: there is no "non-ssa" mode for it. However, SSA construction requires non-trivial algorithms and data structures, so it is inconvenient and wasteful for every front-end to have to reproduce this logic.
The 'trick' here is that while LLVM does require all register values to be in SSA form, it does not require (or permit) memory objects to be in SSA form. In the example above, note that the loads from G and H are direct accesses to G and H: they are not renamed or versioned. This differs from some other compiler systems, which do try to version memory objects. In LLVM, instead of encoding dataflow analysis of memory into the LLVM IR, it is handled with Analysis Passes which are computed on demand.
With this in mind, the high-level idea is that we want to make a stack variable (which lives in memory, because it is on the stack) for each mutable object in a function. To take advantage of this trick, we need to talk about how LLVM represents stack variables.
In LLVM, all memory accesses are explicit with load/store instructions, and it is carefully designed not to have (or need) an "address-of" operator. Notice how the type of the @G/@H global variables is actually "i32*" even though the variable is defined as "i32". What this means is that @G defines space for an i32 in the global data area, but its name actually refers to the address for that space. Stack variables work the same way, except that instead of being declared with global variable definitions, they are declared with the LLVM alloca instruction:
define i32 @example() { entry: %X = alloca i32 ; type of %X is i32*. ... %tmp = load i32* %X ; load the stack value %X from the stack. %tmp2 = add i32 %tmp, 1 ; increment it store i32 %tmp2, i32* %X ; store it back ...
This code shows an example of how you can declare and manipulate a stack variable in the LLVM IR. Stack memory allocated with the alloca instruction is fully general: you can pass the address of the stack slot to functions, you can store it in other variables, etc. In our example above, we could rewrite the example to use the alloca technique to avoid using a PHI node:
@G = weak global i32 0 ; type of @G is i32* @H = weak global i32 0 ; type of @H is i32* define i32 @test(i1 %Condition) { entry: %X = alloca i32 ; type of %X is i32*. br i1 %Condition, label %cond_true, label %cond_false cond_true: %X.0 = load i32* @G store i32 %X.0, i32* %X ; Update X br label %cond_next cond_false: %X.1 = load i32* @H store i32 %X.1, i32* %X ; Update X br label %cond_next cond_next: %X.2 = load i32* %X ; Read X ret i32 %X.2 }
With this, we have discovered a way to handle arbitrary mutable variables without the need to create Phi nodes at all:
While this solution has solved our immediate problem, it introduced another one: we have now apparently introduced a lot of stack traffic for very simple and common operations, a major performance problem. Fortunately for us, the LLVM optimizer has a highly-tuned optimization pass named "mem2reg" that handles this case, promoting allocas like this into SSA registers, inserting Phi nodes as appropriate. If you run this example through the pass, for example, you'll get:
$ llvm-as < example.ll | opt -mem2reg | llvm-dis @G = weak global i32 0 @H = weak global i32 0 define i32 @test(i1 %Condition) { entry: br i1 %Condition, label %cond_true, label %cond_false cond_true: %X.0 = load i32* @G br label %cond_next cond_false: %X.1 = load i32* @H br label %cond_next cond_next: %X.01 = phi i32 [ %X.1, %cond_false ], [ %X.0, %cond_true ] ret i32 %X.01 }
The mem2reg pass implements the standard "iterated dominance frontier" algorithm for constructing SSA form and has a number of optimizations that speed up (very common) degenerate cases. The mem2reg optimization pass is the answer to dealing with mutable variables, and we highly recommend that you depend on it. Note that mem2reg only works on variables in certain circumstances:
All of these properties are easy to satisfy for most imperative languages, and we'll illustrate it below with Kaleidoscope. The final question you may be asking is: should I bother with this nonsense for my front-end? Wouldn't it be better if I just did SSA construction directly, avoiding use of the mem2reg optimization pass? In short, we strongly recommend that you use this technique for building SSA form, unless there is an extremely good reason not to. Using this technique is:
If nothing else, this makes it much easier to get your front-end up and running, and is very simple to implement. Lets extend Kaleidoscope with mutable variables now!
Now that we know the sort of problem we want to tackle, lets see what this looks like in the context of our little Kaleidoscope language. We're going to add two features:
While the first item is really what this is about, we only have variables for incoming arguments as well as for induction variables, and redefining those only goes so far :). Also, the ability to define new variables is a useful thing regardless of whether you will be mutating them. Here's a motivating example that shows how we could use these:
# Define ':' for sequencing: as a low-precedence operator that ignores operands # and just returns the RHS. def binary : 1 (x y) y; # Recursive fib, we could do this before. def fib(x) if (x < 3) then 1 else fib(x-1)+fib(x-2); # Iterative fib. def fibi(x) var a = 1, b = 1, c in (for i = 3, i < x in c = a + b : a = b : b = c) : b; # Call it. fibi(10);
In order to mutate variables, we have to change our existing variables to use the "alloca trick". Once we have that, we'll add our new operator, then extend Kaleidoscope to support new variable definitions.
The symbol table in Kaleidoscope is managed at code generation time by the 'NamedValues' map. This map currently keeps track of the LLVM "Value*" that holds the double value for the named variable. In order to support mutation, we need to change this slightly, so that it NamedValues holds the memory location of the variable in question. Note that this change is a refactoring: it changes the structure of the code, but does not (by itself) change the behavior of the compiler. All of these changes are isolated in the Kaleidoscope code generator.
At this point in Kaleidoscope's development, it only supports variables for two things: incoming arguments to functions and the induction variable of 'for' loops. For consistency, we'll allow mutation of these variables in addition to other user-defined variables. This means that these will both need memory locations.
To start our transformation of Kaleidoscope, we'll change the NamedValues map so that it maps to AllocaInst* instead of Value*. Once we do this, the C++ compiler will tell us what parts of the code we need to update:
static std::map<std::string, AllocaInst*> NamedValues;
Also, since we will need to create these alloca's, we'll use a helper function that ensures that the allocas are created in the entry block of the function:
/// CreateEntryBlockAlloca - Create an alloca instruction in the entry block of /// the function. This is used for mutable variables etc. static AllocaInst *CreateEntryBlockAlloca(Function *TheFunction, const std::string &VarName) { IRBuilder<> TmpB(&TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin()); return TmpB.CreateAlloca(Type::getDoubleTy(getGlobalContext()), 0, VarName.c_str()); }
This funny looking code creates an IRBuilder object that is pointing at the first instruction (.begin()) of the entry block. It then creates an alloca with the expected name and returns it. Because all values in Kaleidoscope are doubles, there is no need to pass in a type to use.
With this in place, the first functionality change we want to make is to variable references. In our new scheme, variables live on the stack, so code generating a reference to them actually needs to produce a load from the stack slot:
Value *VariableExprAST::Codegen() { // Look this variable up in the function. Value *V = NamedValues[Name]; if (V == 0) return ErrorV("Unknown variable name"); // Load the value. return Builder.CreateLoad(V, Name.c_str()); }
As you can see, this is pretty straightforward. Now we need to update the things that define the variables to set up the alloca. We'll start with ForExprAST::Codegen (see the full code listing for the unabridged code):
Function *TheFunction = Builder.GetInsertBlock()->getParent(); // Create an alloca for the variable in the entry block. AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, VarName); // Emit the start code first, without 'variable' in scope. Value *StartVal = Start->Codegen(); if (StartVal == 0) return 0; // Store the value into the alloca. Builder.CreateStore(StartVal, Alloca); ... // Compute the end condition. Value *EndCond = End->Codegen(); if (EndCond == 0) return EndCond; // Reload, increment, and restore the alloca. This handles the case where // the body of the loop mutates the variable. Value *CurVar = Builder.CreateLoad(Alloca); Value *NextVar = Builder.CreateFAdd(CurVar, StepVal, "nextvar"); Builder.CreateStore(NextVar, Alloca); ...
This code is virtually identical to the code before we allowed mutable variables. The big difference is that we no longer have to construct a PHI node, and we use load/store to access the variable as needed.
To support mutable argument variables, we need to also make allocas for them. The code for this is also pretty simple:
/// CreateArgumentAllocas - Create an alloca for each argument and register the /// argument in the symbol table so that references to it will succeed. void PrototypeAST::CreateArgumentAllocas(Function *F) { Function::arg_iterator AI = F->arg_begin(); for (unsigned Idx = 0, e = Args.size(); Idx != e; ++Idx, ++AI) { // Create an alloca for this variable. AllocaInst *Alloca = CreateEntryBlockAlloca(F, Args[Idx]); // Store the initial value into the alloca. Builder.CreateStore(AI, Alloca); // Add arguments to variable symbol table. NamedValues[Args[Idx]] = Alloca; } }
For each argument, we make an alloca, store the input value to the function into the alloca, and register the alloca as the memory location for the argument. This method gets invoked by FunctionAST::Codegen right after it sets up the entry block for the function.
The final missing piece is adding the mem2reg pass, which allows us to get good codegen once again:
// Set up the optimizer pipeline. Start with registering info about how the // target lays out data structures. OurFPM.add(new TargetData(*TheExecutionEngine->getTargetData())); // Promote allocas to registers. OurFPM.add(createPromoteMemoryToRegisterPass()); // Do simple "peephole" optimizations and bit-twiddling optzns. OurFPM.add(createInstructionCombiningPass()); // Reassociate expressions. OurFPM.add(createReassociatePass());
It is interesting to see what the code looks like before and after the mem2reg optimization runs. For example, this is the before/after code for our recursive fib function. Before the optimization:
define double @fib(double %x) { entry: %x1 = alloca double store double %x, double* %x1 %x2 = load double* %x1 %cmptmp = fcmp ult double %x2, 3.000000e+00 %booltmp = uitofp i1 %cmptmp to double %ifcond = fcmp one double %booltmp, 0.000000e+00 br i1 %ifcond, label %then, label %else then: ; preds = %entry br label %ifcont else: ; preds = %entry %x3 = load double* %x1 %subtmp = fsub double %x3, 1.000000e+00 %calltmp = call double @fib(double %subtmp) %x4 = load double* %x1 %subtmp5 = fsub double %x4, 2.000000e+00 %calltmp6 = call double @fib(double %subtmp5) %addtmp = fadd double %calltmp, %calltmp6 br label %ifcont ifcont: ; preds = %else, %then %iftmp = phi double [ 1.000000e+00, %then ], [ %addtmp, %else ] ret double %iftmp }
Here there is only one variable (x, the input argument) but you can still see the extremely simple-minded code generation strategy we are using. In the entry block, an alloca is created, and the initial input value is stored into it. Each reference to the variable does a reload from the stack. Also, note that we didn't modify the if/then/else expression, so it still inserts a PHI node. While we could make an alloca for it, it is actually easier to create a PHI node for it, so we still just make the PHI.
Here is the code after the mem2reg pass runs:
define double @fib(double %x) { entry: %cmptmp = fcmp ult double %x, 3.000000e+00 %booltmp = uitofp i1 %cmptmp to double %ifcond = fcmp one double %booltmp, 0.000000e+00 br i1 %ifcond, label %then, label %else then: br label %ifcont else: %subtmp = fsub double %x, 1.000000e+00 %calltmp = call double @fib(double %subtmp) %subtmp5 = fsub double %x, 2.000000e+00 %calltmp6 = call double @fib(double %subtmp5) %addtmp = fadd double %calltmp, %calltmp6 br label %ifcont ifcont: ; preds = %else, %then %iftmp = phi double [ 1.000000e+00, %then ], [ %addtmp, %else ] ret double %iftmp }
This is a trivial case for mem2reg, since there are no redefinitions of the variable. The point of showing this is to calm your tension about inserting such blatent inefficiencies :).
After the rest of the optimizers run, we get:
define double @fib(double %x) { entry: %cmptmp = fcmp ult double %x, 3.000000e+00 %booltmp = uitofp i1 %cmptmp to double %ifcond = fcmp ueq double %booltmp, 0.000000e+00 br i1 %ifcond, label %else, label %ifcont else: %subtmp = fsub double %x, 1.000000e+00 %calltmp = call double @fib(double %subtmp) %subtmp5 = fsub double %x, 2.000000e+00 %calltmp6 = call double @fib(double %subtmp5) %addtmp = fadd double %calltmp, %calltmp6 ret double %addtmp ifcont: ret double 1.000000e+00 }
Here we see that the simplifycfg pass decided to clone the return instruction into the end of the 'else' block. This allowed it to eliminate some branches and the PHI node.
Now that all symbol table references are updated to use stack variables, we'll add the assignment operator.
With our current framework, adding a new assignment operator is really simple. We will parse it just like any other binary operator, but handle it internally (instead of allowing the user to define it). The first step is to set a precedence:
int main() { // Install standard binary operators. // 1 is lowest precedence. BinopPrecedence['='] = 2; BinopPrecedence['<'] = 10; BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20;
Now that the parser knows the precedence of the binary operator, it takes care of all the parsing and AST generation. We just need to implement codegen for the assignment operator. This looks like:
Value *BinaryExprAST::Codegen() { // Special case '=' because we don't want to emit the LHS as an expression. if (Op == '=') { // Assignment requires the LHS to be an identifier. VariableExprAST *LHSE = dynamic_cast<VariableExprAST*>(LHS); if (!LHSE) return ErrorV("destination of '=' must be a variable");
Unlike the rest of the binary operators, our assignment operator doesn't follow the "emit LHS, emit RHS, do computation" model. As such, it is handled as a special case before the other binary operators are handled. The other strange thing is that it requires the LHS to be a variable. It is invalid to have "(x+1) = expr" - only things like "x = expr" are allowed.
// Codegen the RHS. Value *Val = RHS->Codegen(); if (Val == 0) return 0; // Look up the name. Value *Variable = NamedValues[LHSE->getName()]; if (Variable == 0) return ErrorV("Unknown variable name"); Builder.CreateStore(Val, Variable); return Val; } ...
Once we have the variable, codegen'ing the assignment is straightforward: we emit the RHS of the assignment, create a store, and return the computed value. Returning a value allows for chained assignments like "X = (Y = Z)".
Now that we have an assignment operator, we can mutate loop variables and arguments. For example, we can now run code like this:
# Function to print a double. extern printd(x); # Define ':' for sequencing: as a low-precedence operator that ignores operands # and just returns the RHS. def binary : 1 (x y) y; def test(x) printd(x) : x = 4 : printd(x); test(123);
When run, this example prints "123" and then "4", showing that we did actually mutate the value! Okay, we have now officially implemented our goal: getting this to work requires SSA construction in the general case. However, to be really useful, we want the ability to define our own local variables, lets add this next!
Adding var/in is just like any other other extensions we made to Kaleidoscope: we extend the lexer, the parser, the AST and the code generator. The first step for adding our new 'var/in' construct is to extend the lexer. As before, this is pretty trivial, the code looks like this:
enum Token { ... // var definition tok_var = -13 ... } ... static int gettok() { ... if (IdentifierStr == "in") return tok_in; if (IdentifierStr == "binary") return tok_binary; if (IdentifierStr == "unary") return tok_unary; if (IdentifierStr == "var") return tok_var; return tok_identifier; ...
The next step is to define the AST node that we will construct. For var/in, it looks like this:
/// VarExprAST - Expression class for var/in class VarExprAST : public ExprAST { std::vector<std::pair<std::string, ExprAST*> > VarNames; ExprAST *Body; public: VarExprAST(const std::vector<std::pair<std::string, ExprAST*> > &varnames, ExprAST *body) : VarNames(varnames), Body(body) {} virtual Value *Codegen(); };
var/in allows a list of names to be defined all at once, and each name can optionally have an initializer value. As such, we capture this information in the VarNames vector. Also, var/in has a body, this body is allowed to access the variables defined by the var/in.
With this in place, we can define the parser pieces. The first thing we do is add it as a primary expression:
/// primary /// ::= identifierexpr /// ::= numberexpr /// ::= parenexpr /// ::= ifexpr /// ::= forexpr /// ::= varexpr static ExprAST *ParsePrimary() { switch (CurTok) { default: return Error("unknown token when expecting an expression"); case tok_identifier: return ParseIdentifierExpr(); case tok_number: return ParseNumberExpr(); case '(': return ParseParenExpr(); case tok_if: return ParseIfExpr(); case tok_for: return ParseForExpr(); case tok_var: return ParseVarExpr(); } }
Next we define ParseVarExpr:
/// varexpr ::= 'var' identifier ('=' expression)? // (',' identifier ('=' expression)?)* 'in' expression static ExprAST *ParseVarExpr() { getNextToken(); // eat the var. std::vector<std::pair<std::string, ExprAST*> > VarNames; // At least one variable name is required. if (CurTok != tok_identifier) return Error("expected identifier after var");
The first part of this code parses the list of identifier/expr pairs into the local VarNames vector.
while (1) { std::string Name = IdentifierStr; getNextToken(); // eat identifier. // Read the optional initializer. ExprAST *Init = 0; if (CurTok == '=') { getNextToken(); // eat the '='. Init = ParseExpression(); if (Init == 0) return 0; } VarNames.push_back(std::make_pair(Name, Init)); // End of var list, exit loop. if (CurTok != ',') break; getNextToken(); // eat the ','. if (CurTok != tok_identifier) return Error("expected identifier list after var"); }
Once all the variables are parsed, we then parse the body and create the AST node:
// At this point, we have to have 'in'. if (CurTok != tok_in) return Error("expected 'in' keyword after 'var'"); getNextToken(); // eat 'in'. ExprAST *Body = ParseExpression(); if (Body == 0) return 0; return new VarExprAST(VarNames, Body); }
Now that we can parse and represent the code, we need to support emission of LLVM IR for it. This code starts out with:
Value *VarExprAST::Codegen() { std::vector<AllocaInst *> OldBindings; Function *TheFunction = Builder.GetInsertBlock()->getParent(); // Register all variables and emit their initializer. for (unsigned i = 0, e = VarNames.size(); i != e; ++i) { const std::string &VarName = VarNames[i].first; ExprAST *Init = VarNames[i].second;
Basically it loops over all the variables, installing them one at a time. For each variable we put into the symbol table, we remember the previous value that we replace in OldBindings.
// Emit the initializer before adding the variable to scope, this prevents // the initializer from referencing the variable itself, and permits stuff // like this: // var a = 1 in // var a = a in ... # refers to outer 'a'. Value *InitVal; if (Init) { InitVal = Init->Codegen(); if (InitVal == 0) return 0; } else { // If not specified, use 0.0. InitVal = ConstantFP::get(getGlobalContext(), APFloat(0.0)); } AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, VarName); Builder.CreateStore(InitVal, Alloca); // Remember the old variable binding so that we can restore the binding when // we unrecurse. OldBindings.push_back(NamedValues[VarName]); // Remember this binding. NamedValues[VarName] = Alloca; }
There are more comments here than code. The basic idea is that we emit the initializer, create the alloca, then update the symbol table to point to it. Once all the variables are installed in the symbol table, we evaluate the body of the var/in expression:
// Codegen the body, now that all vars are in scope. Value *BodyVal = Body->Codegen(); if (BodyVal == 0) return 0;
Finally, before returning, we restore the previous variable bindings:
// Pop all our variables from scope. for (unsigned i = 0, e = VarNames.size(); i != e; ++i) NamedValues[VarNames[i].first] = OldBindings[i]; // Return the body computation. return BodyVal; }
The end result of all of this is that we get properly scoped variable definitions, and we even (trivially) allow mutation of them :).
With this, we completed what we set out to do. Our nice iterative fib example from the intro compiles and runs just fine. The mem2reg pass optimizes all of our stack variables into SSA registers, inserting PHI nodes where needed, and our front-end remains simple: no "iterated dominance frontier" computation anywhere in sight.
Here is the complete code listing for our running example, enhanced with mutable variables and var/in support. To build this example, use:
# Compile clang++ -g toy.cpp `llvm-config --cppflags --ldflags --libs core jit native` -O3 -o toy # Run ./toy
Here is the code:
#include "llvm/DerivedTypes.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/JIT.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/PassManager.h" #include "llvm/Analysis/Verifier.h" #include "llvm/Analysis/Passes.h" #include "llvm/Target/TargetData.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Support/IRBuilder.h" #include "llvm/Support/TargetSelect.h" #include <cstdio> #include <string> #include <map> #include <vector> using namespace llvm; //===----------------------------------------------------------------------===// // Lexer //===----------------------------------------------------------------------===// // The lexer returns tokens [0-255] if it is an unknown character, otherwise one // of these for known things. enum Token { tok_eof = -1, // commands tok_def = -2, tok_extern = -3, // primary tok_identifier = -4, tok_number = -5, // control tok_if = -6, tok_then = -7, tok_else = -8, tok_for = -9, tok_in = -10, // operators tok_binary = -11, tok_unary = -12, // var definition tok_var = -13 }; static std::string IdentifierStr; // Filled in if tok_identifier static double NumVal; // Filled in if tok_number /// gettok - Return the next token from standard input. static int gettok() { static int LastChar = ' '; // Skip any whitespace. while (isspace(LastChar)) LastChar = getchar(); if (isalpha(LastChar)) { // identifier: [a-zA-Z][a-zA-Z0-9]* IdentifierStr = LastChar; while (isalnum((LastChar = getchar()))) IdentifierStr += LastChar; if (IdentifierStr == "def") return tok_def; if (IdentifierStr == "extern") return tok_extern; if (IdentifierStr == "if") return tok_if; if (IdentifierStr == "then") return tok_then; if (IdentifierStr == "else") return tok_else; if (IdentifierStr == "for") return tok_for; if (IdentifierStr == "in") return tok_in; if (IdentifierStr == "binary") return tok_binary; if (IdentifierStr == "unary") return tok_unary; if (IdentifierStr == "var") return tok_var; return tok_identifier; } if (isdigit(LastChar) || LastChar == '.') { // Number: [0-9.]+ std::string NumStr; do { NumStr += LastChar; LastChar = getchar(); } while (isdigit(LastChar) || LastChar == '.'); NumVal = strtod(NumStr.c_str(), 0); return tok_number; } if (LastChar == '#') { // Comment until end of line. do LastChar = getchar(); while (LastChar != EOF && LastChar != '\n' && LastChar != '\r'); if (LastChar != EOF) return gettok(); } // Check for end of file. Don't eat the EOF. if (LastChar == EOF) return tok_eof; // Otherwise, just return the character as its ascii value. int ThisChar = LastChar; LastChar = getchar(); return ThisChar; } //===----------------------------------------------------------------------===// // Abstract Syntax Tree (aka Parse Tree) //===----------------------------------------------------------------------===// /// ExprAST - Base class for all expression nodes. class ExprAST { public: virtual ~ExprAST() {} virtual Value *Codegen() = 0; }; /// NumberExprAST - Expression class for numeric literals like "1.0". class NumberExprAST : public ExprAST { double Val; public: NumberExprAST(double val) : Val(val) {} virtual Value *Codegen(); }; /// VariableExprAST - Expression class for referencing a variable, like "a". class VariableExprAST : public ExprAST { std::string Name; public: VariableExprAST(const std::string &name) : Name(name) {} const std::string &getName() const { return Name; } virtual Value *Codegen(); }; /// UnaryExprAST - Expression class for a unary operator. class UnaryExprAST : public ExprAST { char Opcode; ExprAST *Operand; public: UnaryExprAST(char opcode, ExprAST *operand) : Opcode(opcode), Operand(operand) {} virtual Value *Codegen(); }; /// BinaryExprAST - Expression class for a binary operator. class BinaryExprAST : public ExprAST { char Op; ExprAST *LHS, *RHS; public: BinaryExprAST(char op, ExprAST *lhs, ExprAST *rhs) : Op(op), LHS(lhs), RHS(rhs) {} virtual Value *Codegen(); }; /// CallExprAST - Expression class for function calls. class CallExprAST : public ExprAST { std::string Callee; std::vector<ExprAST*> Args; public: CallExprAST(const std::string &callee, std::vector<ExprAST*> &args) : Callee(callee), Args(args) {} virtual Value *Codegen(); }; /// IfExprAST - Expression class for if/then/else. class IfExprAST : public ExprAST { ExprAST *Cond, *Then, *Else; public: IfExprAST(ExprAST *cond, ExprAST *then, ExprAST *_else) : Cond(cond), Then(then), Else(_else) {} virtual Value *Codegen(); }; /// ForExprAST - Expression class for for/in. class ForExprAST : public ExprAST { std::string VarName; ExprAST *Start, *End, *Step, *Body; public: ForExprAST(const std::string &varname, ExprAST *start, ExprAST *end, ExprAST *step, ExprAST *body) : VarName(varname), Start(start), End(end), Step(step), Body(body) {} virtual Value *Codegen(); }; /// VarExprAST - Expression class for var/in class VarExprAST : public ExprAST { std::vector<std::pair<std::string, ExprAST*> > VarNames; ExprAST *Body; public: VarExprAST(const std::vector<std::pair<std::string, ExprAST*> > &varnames, ExprAST *body) : VarNames(varnames), Body(body) {} virtual Value *Codegen(); }; /// PrototypeAST - This class represents the "prototype" for a function, /// which captures its name, and its argument names (thus implicitly the number /// of arguments the function takes), as well as if it is an operator. class PrototypeAST { std::string Name; std::vector<std::string> Args; bool isOperator; unsigned Precedence; // Precedence if a binary op. public: PrototypeAST(const std::string &name, const std::vector<std::string> &args, bool isoperator = false, unsigned prec = 0) : Name(name), Args(args), isOperator(isoperator), Precedence(prec) {} bool isUnaryOp() const { return isOperator && Args.size() == 1; } bool isBinaryOp() const { return isOperator && Args.size() == 2; } char getOperatorName() const { assert(isUnaryOp() || isBinaryOp()); return Name[Name.size()-1]; } unsigned getBinaryPrecedence() const { return Precedence; } Function *Codegen(); void CreateArgumentAllocas(Function *F); }; /// FunctionAST - This class represents a function definition itself. class FunctionAST { PrototypeAST *Proto; ExprAST *Body; public: FunctionAST(PrototypeAST *proto, ExprAST *body) : Proto(proto), Body(body) {} Function *Codegen(); }; //===----------------------------------------------------------------------===// // Parser //===----------------------------------------------------------------------===// /// CurTok/getNextToken - Provide a simple token buffer. CurTok is the current /// token the parser is looking at. getNextToken reads another token from the /// lexer and updates CurTok with its results. static int CurTok; static int getNextToken() { return CurTok = gettok(); } /// BinopPrecedence - This holds the precedence for each binary operator that is /// defined. static std::map<char, int> BinopPrecedence; /// GetTokPrecedence - Get the precedence of the pending binary operator token. static int GetTokPrecedence() { if (!isascii(CurTok)) return -1; // Make sure it's a declared binop. int TokPrec = BinopPrecedence[CurTok]; if (TokPrec <= 0) return -1; return TokPrec; } /// Error* - These are little helper functions for error handling. ExprAST *Error(const char *Str) { fprintf(stderr, "Error: %s\n", Str);return 0;} PrototypeAST *ErrorP(const char *Str) { Error(Str); return 0; } FunctionAST *ErrorF(const char *Str) { Error(Str); return 0; } static ExprAST *ParseExpression(); /// identifierexpr /// ::= identifier /// ::= identifier '(' expression* ')' static ExprAST *ParseIdentifierExpr() { std::string IdName = IdentifierStr; getNextToken(); // eat identifier. if (CurTok != '(') // Simple variable ref. return new VariableExprAST(IdName); // Call. getNextToken(); // eat ( std::vector<ExprAST*> Args; if (CurTok != ')') { while (1) { ExprAST *Arg = ParseExpression(); if (!Arg) return 0; Args.push_back(Arg); if (CurTok == ')') break; if (CurTok != ',') return Error("Expected ')' or ',' in argument list"); getNextToken(); } } // Eat the ')'. getNextToken(); return new CallExprAST(IdName, Args); } /// numberexpr ::= number static ExprAST *ParseNumberExpr() { ExprAST *Result = new NumberExprAST(NumVal); getNextToken(); // consume the number return Result; } /// parenexpr ::= '(' expression ')' static ExprAST *ParseParenExpr() { getNextToken(); // eat (. ExprAST *V = ParseExpression(); if (!V) return 0; if (CurTok != ')') return Error("expected ')'"); getNextToken(); // eat ). return V; } /// ifexpr ::= 'if' expression 'then' expression 'else' expression static ExprAST *ParseIfExpr() { getNextToken(); // eat the if. // condition. ExprAST *Cond = ParseExpression(); if (!Cond) return 0; if (CurTok != tok_then) return Error("expected then"); getNextToken(); // eat the then ExprAST *Then = ParseExpression(); if (Then == 0) return 0; if (CurTok != tok_else) return Error("expected else"); getNextToken(); ExprAST *Else = ParseExpression(); if (!Else) return 0; return new IfExprAST(Cond, Then, Else); } /// forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression static ExprAST *ParseForExpr() { getNextToken(); // eat the for. if (CurTok != tok_identifier) return Error("expected identifier after for"); std::string IdName = IdentifierStr; getNextToken(); // eat identifier. if (CurTok != '=') return Error("expected '=' after for"); getNextToken(); // eat '='. ExprAST *Start = ParseExpression(); if (Start == 0) return 0; if (CurTok != ',') return Error("expected ',' after for start value"); getNextToken(); ExprAST *End = ParseExpression(); if (End == 0) return 0; // The step value is optional. ExprAST *Step = 0; if (CurTok == ',') { getNextToken(); Step = ParseExpression(); if (Step == 0) return 0; } if (CurTok != tok_in) return Error("expected 'in' after for"); getNextToken(); // eat 'in'. ExprAST *Body = ParseExpression(); if (Body == 0) return 0; return new ForExprAST(IdName, Start, End, Step, Body); } /// varexpr ::= 'var' identifier ('=' expression)? // (',' identifier ('=' expression)?)* 'in' expression static ExprAST *ParseVarExpr() { getNextToken(); // eat the var. std::vector<std::pair<std::string, ExprAST*> > VarNames; // At least one variable name is required. if (CurTok != tok_identifier) return Error("expected identifier after var"); while (1) { std::string Name = IdentifierStr; getNextToken(); // eat identifier. // Read the optional initializer. ExprAST *Init = 0; if (CurTok == '=') { getNextToken(); // eat the '='. Init = ParseExpression(); if (Init == 0) return 0; } VarNames.push_back(std::make_pair(Name, Init)); // End of var list, exit loop. if (CurTok != ',') break; getNextToken(); // eat the ','. if (CurTok != tok_identifier) return Error("expected identifier list after var"); } // At this point, we have to have 'in'. if (CurTok != tok_in) return Error("expected 'in' keyword after 'var'"); getNextToken(); // eat 'in'. ExprAST *Body = ParseExpression(); if (Body == 0) return 0; return new VarExprAST(VarNames, Body); } /// primary /// ::= identifierexpr /// ::= numberexpr /// ::= parenexpr /// ::= ifexpr /// ::= forexpr /// ::= varexpr static ExprAST *ParsePrimary() { switch (CurTok) { default: return Error("unknown token when expecting an expression"); case tok_identifier: return ParseIdentifierExpr(); case tok_number: return ParseNumberExpr(); case '(': return ParseParenExpr(); case tok_if: return ParseIfExpr(); case tok_for: return ParseForExpr(); case tok_var: return ParseVarExpr(); } } /// unary /// ::= primary /// ::= '!' unary static ExprAST *ParseUnary() { // If the current token is not an operator, it must be a primary expr. if (!isascii(CurTok) || CurTok == '(' || CurTok == ',') return ParsePrimary(); // If this is a unary operator, read it. int Opc = CurTok; getNextToken(); if (ExprAST *Operand = ParseUnary()) return new UnaryExprAST(Opc, Operand); return 0; } /// binoprhs /// ::= ('+' unary)* static ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS) { // If this is a binop, find its precedence. while (1) { int TokPrec = GetTokPrecedence(); // If this is a binop that binds at least as tightly as the current binop, // consume it, otherwise we are done. if (TokPrec < ExprPrec) return LHS; // Okay, we know this is a binop. int BinOp = CurTok; getNextToken(); // eat binop // Parse the unary expression after the binary operator. ExprAST *RHS = ParseUnary(); if (!RHS) return 0; // If BinOp binds less tightly with RHS than the operator after RHS, let // the pending operator take RHS as its LHS. int NextPrec = GetTokPrecedence(); if (TokPrec < NextPrec) { RHS = ParseBinOpRHS(TokPrec+1, RHS); if (RHS == 0) return 0; } // Merge LHS/RHS. LHS = new BinaryExprAST(BinOp, LHS, RHS); } } /// expression /// ::= unary binoprhs /// static ExprAST *ParseExpression() { ExprAST *LHS = ParseUnary(); if (!LHS) return 0; return ParseBinOpRHS(0, LHS); } /// prototype /// ::= id '(' id* ')' /// ::= binary LETTER number? (id, id) /// ::= unary LETTER (id) static PrototypeAST *ParsePrototype() { std::string FnName; unsigned Kind = 0; // 0 = identifier, 1 = unary, 2 = binary. unsigned BinaryPrecedence = 30; switch (CurTok) { default: return ErrorP("Expected function name in prototype"); case tok_identifier: FnName = IdentifierStr; Kind = 0; getNextToken(); break; case tok_unary: getNextToken(); if (!isascii(CurTok)) return ErrorP("Expected unary operator"); FnName = "unary"; FnName += (char)CurTok; Kind = 1; getNextToken(); break; case tok_binary: getNextToken(); if (!isascii(CurTok)) return ErrorP("Expected binary operator"); FnName = "binary"; FnName += (char)CurTok; Kind = 2; getNextToken(); // Read the precedence if present. if (CurTok == tok_number) { if (NumVal < 1 || NumVal > 100) return ErrorP("Invalid precedecnce: must be 1..100"); BinaryPrecedence = (unsigned)NumVal; getNextToken(); } break; } if (CurTok != '(') return ErrorP("Expected '(' in prototype"); std::vector<std::string> ArgNames; while (getNextToken() == tok_identifier) ArgNames.push_back(IdentifierStr); if (CurTok != ')') return ErrorP("Expected ')' in prototype"); // success. getNextToken(); // eat ')'. // Verify right number of names for operator. if (Kind && ArgNames.size() != Kind) return ErrorP("Invalid number of operands for operator"); return new PrototypeAST(FnName, ArgNames, Kind != 0, BinaryPrecedence); } /// definition ::= 'def' prototype expression static FunctionAST *ParseDefinition() { getNextToken(); // eat def. PrototypeAST *Proto = ParsePrototype(); if (Proto == 0) return 0; if (ExprAST *E = ParseExpression()) return new FunctionAST(Proto, E); return 0; } /// toplevelexpr ::= expression static FunctionAST *ParseTopLevelExpr() { if (ExprAST *E = ParseExpression()) { // Make an anonymous proto. PrototypeAST *Proto = new PrototypeAST("", std::vector<std::string>()); return new FunctionAST(Proto, E); } return 0; } /// external ::= 'extern' prototype static PrototypeAST *ParseExtern() { getNextToken(); // eat extern. return ParsePrototype(); } //===----------------------------------------------------------------------===// // Code Generation //===----------------------------------------------------------------------===// static Module *TheModule; static IRBuilder<> Builder(getGlobalContext()); static std::map<std::string, AllocaInst*> NamedValues; static FunctionPassManager *TheFPM; Value *ErrorV(const char *Str) { Error(Str); return 0; } /// CreateEntryBlockAlloca - Create an alloca instruction in the entry block of /// the function. This is used for mutable variables etc. static AllocaInst *CreateEntryBlockAlloca(Function *TheFunction, const std::string &VarName) { IRBuilder<> TmpB(&TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin()); return TmpB.CreateAlloca(Type::getDoubleTy(getGlobalContext()), 0, VarName.c_str()); } Value *NumberExprAST::Codegen() { return ConstantFP::get(getGlobalContext(), APFloat(Val)); } Value *VariableExprAST::Codegen() { // Look this variable up in the function. Value *V = NamedValues[Name]; if (V == 0) return ErrorV("Unknown variable name"); // Load the value. return Builder.CreateLoad(V, Name.c_str()); } Value *UnaryExprAST::Codegen() { Value *OperandV = Operand->Codegen(); if (OperandV == 0) return 0; Function *F = TheModule->getFunction(std::string("unary")+Opcode); if (F == 0) return ErrorV("Unknown unary operator"); return Builder.CreateCall(F, OperandV, "unop"); } Value *BinaryExprAST::Codegen() { // Special case '=' because we don't want to emit the LHS as an expression. if (Op == '=') { // Assignment requires the LHS to be an identifier. VariableExprAST *LHSE = dynamic_cast<VariableExprAST*>(LHS); if (!LHSE) return ErrorV("destination of '=' must be a variable"); // Codegen the RHS. Value *Val = RHS->Codegen(); if (Val == 0) return 0; // Look up the name. Value *Variable = NamedValues[LHSE->getName()]; if (Variable == 0) return ErrorV("Unknown variable name"); Builder.CreateStore(Val, Variable); return Val; } Value *L = LHS->Codegen(); Value *R = RHS->Codegen(); if (L == 0 || R == 0) return 0; switch (Op) { case '+': return Builder.CreateFAdd(L, R, "addtmp"); case '-': return Builder.CreateFSub(L, R, "subtmp"); case '*': return Builder.CreateFMul(L, R, "multmp"); case '<': L = Builder.CreateFCmpULT(L, R, "cmptmp"); // Convert bool 0/1 to double 0.0 or 1.0 return Builder.CreateUIToFP(L, Type::getDoubleTy(getGlobalContext()), "booltmp"); default: break; } // If it wasn't a builtin binary operator, it must be a user defined one. Emit // a call to it. Function *F = TheModule->getFunction(std::string("binary")+Op); assert(F && "binary operator not found!"); Value *Ops[2] = { L, R }; return Builder.CreateCall(F, Ops, "binop"); } Value *CallExprAST::Codegen() { // Look up the name in the global module table. Function *CalleeF = TheModule->getFunction(Callee); if (CalleeF == 0) return ErrorV("Unknown function referenced"); // If argument mismatch error. if (CalleeF->arg_size() != Args.size()) return ErrorV("Incorrect # arguments passed"); std::vector<Value*> ArgsV; for (unsigned i = 0, e = Args.size(); i != e; ++i) { ArgsV.push_back(Args[i]->Codegen()); if (ArgsV.back() == 0) return 0; } return Builder.CreateCall(CalleeF, ArgsV, "calltmp"); } Value *IfExprAST::Codegen() { Value *CondV = Cond->Codegen(); if (CondV == 0) return 0; // Convert condition to a bool by comparing equal to 0.0. CondV = Builder.CreateFCmpONE(CondV, ConstantFP::get(getGlobalContext(), APFloat(0.0)), "ifcond"); Function *TheFunction = Builder.GetInsertBlock()->getParent(); // Create blocks for the then and else cases. Insert the 'then' block at the // end of the function. BasicBlock *ThenBB = BasicBlock::Create(getGlobalContext(), "then", TheFunction); BasicBlock *ElseBB = BasicBlock::Create(getGlobalContext(), "else"); BasicBlock *MergeBB = BasicBlock::Create(getGlobalContext(), "ifcont"); Builder.CreateCondBr(CondV, ThenBB, ElseBB); // Emit then value. Builder.SetInsertPoint(ThenBB); Value *ThenV = Then->Codegen(); if (ThenV == 0) return 0; Builder.CreateBr(MergeBB); // Codegen of 'Then' can change the current block, update ThenBB for the PHI. ThenBB = Builder.GetInsertBlock(); // Emit else block. TheFunction->getBasicBlockList().push_back(ElseBB); Builder.SetInsertPoint(ElseBB); Value *ElseV = Else->Codegen(); if (ElseV == 0) return 0; Builder.CreateBr(MergeBB); // Codegen of 'Else' can change the current block, update ElseBB for the PHI. ElseBB = Builder.GetInsertBlock(); // Emit merge block. TheFunction->getBasicBlockList().push_back(MergeBB); Builder.SetInsertPoint(MergeBB); PHINode *PN = Builder.CreatePHI(Type::getDoubleTy(getGlobalContext()), 2, "iftmp"); PN->addIncoming(ThenV, ThenBB); PN->addIncoming(ElseV, ElseBB); return PN; } Value *ForExprAST::Codegen() { // Output this as: // var = alloca double // ... // start = startexpr // store start -> var // goto loop // loop: // ... // bodyexpr // ... // loopend: // step = stepexpr // endcond = endexpr // // curvar = load var // nextvar = curvar + step // store nextvar -> var // br endcond, loop, endloop // outloop: Function *TheFunction = Builder.GetInsertBlock()->getParent(); // Create an alloca for the variable in the entry block. AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, VarName); // Emit the start code first, without 'variable' in scope. Value *StartVal = Start->Codegen(); if (StartVal == 0) return 0; // Store the value into the alloca. Builder.CreateStore(StartVal, Alloca); // Make the new basic block for the loop header, inserting after current // block. BasicBlock *LoopBB = BasicBlock::Create(getGlobalContext(), "loop", TheFunction); // Insert an explicit fall through from the current block to the LoopBB. Builder.CreateBr(LoopBB); // Start insertion in LoopBB. Builder.SetInsertPoint(LoopBB); // Within the loop, the variable is defined equal to the PHI node. If it // shadows an existing variable, we have to restore it, so save it now. AllocaInst *OldVal = NamedValues[VarName]; NamedValues[VarName] = Alloca; // Emit the body of the loop. This, like any other expr, can change the // current BB. Note that we ignore the value computed by the body, but don't // allow an error. if (Body->Codegen() == 0) return 0; // Emit the step value. Value *StepVal; if (Step) { StepVal = Step->Codegen(); if (StepVal == 0) return 0; } else { // If not specified, use 1.0. StepVal = ConstantFP::get(getGlobalContext(), APFloat(1.0)); } // Compute the end condition. Value *EndCond = End->Codegen(); if (EndCond == 0) return EndCond; // Reload, increment, and restore the alloca. This handles the case where // the body of the loop mutates the variable. Value *CurVar = Builder.CreateLoad(Alloca, VarName.c_str()); Value *NextVar = Builder.CreateFAdd(CurVar, StepVal, "nextvar"); Builder.CreateStore(NextVar, Alloca); // Convert condition to a bool by comparing equal to 0.0. EndCond = Builder.CreateFCmpONE(EndCond, ConstantFP::get(getGlobalContext(), APFloat(0.0)), "loopcond"); // Create the "after loop" block and insert it. BasicBlock *AfterBB = BasicBlock::Create(getGlobalContext(), "afterloop", TheFunction); // Insert the conditional branch into the end of LoopEndBB. Builder.CreateCondBr(EndCond, LoopBB, AfterBB); // Any new code will be inserted in AfterBB. Builder.SetInsertPoint(AfterBB); // Restore the unshadowed variable. if (OldVal) NamedValues[VarName] = OldVal; else NamedValues.erase(VarName); // for expr always returns 0.0. return Constant::getNullValue(Type::getDoubleTy(getGlobalContext())); } Value *VarExprAST::Codegen() { std::vector<AllocaInst *> OldBindings; Function *TheFunction = Builder.GetInsertBlock()->getParent(); // Register all variables and emit their initializer. for (unsigned i = 0, e = VarNames.size(); i != e; ++i) { const std::string &VarName = VarNames[i].first; ExprAST *Init = VarNames[i].second; // Emit the initializer before adding the variable to scope, this prevents // the initializer from referencing the variable itself, and permits stuff // like this: // var a = 1 in // var a = a in ... # refers to outer 'a'. Value *InitVal; if (Init) { InitVal = Init->Codegen(); if (InitVal == 0) return 0; } else { // If not specified, use 0.0. InitVal = ConstantFP::get(getGlobalContext(), APFloat(0.0)); } AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, VarName); Builder.CreateStore(InitVal, Alloca); // Remember the old variable binding so that we can restore the binding when // we unrecurse. OldBindings.push_back(NamedValues[VarName]); // Remember this binding. NamedValues[VarName] = Alloca; } // Codegen the body, now that all vars are in scope. Value *BodyVal = Body->Codegen(); if (BodyVal == 0) return 0; // Pop all our variables from scope. for (unsigned i = 0, e = VarNames.size(); i != e; ++i) NamedValues[VarNames[i].first] = OldBindings[i]; // Return the body computation. return BodyVal; } Function *PrototypeAST::Codegen() { // Make the function type: double(double,double) etc. std::vector<Type*> Doubles(Args.size(), Type::getDoubleTy(getGlobalContext())); FunctionType *FT = FunctionType::get(Type::getDoubleTy(getGlobalContext()), Doubles, false); Function *F = Function::Create(FT, Function::ExternalLinkage, Name, TheModule); // If F conflicted, there was already something named 'Name'. If it has a // body, don't allow redefinition or reextern. if (F->getName() != Name) { // Delete the one we just made and get the existing one. F->eraseFromParent(); F = TheModule->getFunction(Name); // If F already has a body, reject this. if (!F->empty()) { ErrorF("redefinition of function"); return 0; } // If F took a different number of args, reject. if (F->arg_size() != Args.size()) { ErrorF("redefinition of function with different # args"); return 0; } } // Set names for all arguments. unsigned Idx = 0; for (Function::arg_iterator AI = F->arg_begin(); Idx != Args.size(); ++AI, ++Idx) AI->setName(Args[Idx]); return F; } /// CreateArgumentAllocas - Create an alloca for each argument and register the /// argument in the symbol table so that references to it will succeed. void PrototypeAST::CreateArgumentAllocas(Function *F) { Function::arg_iterator AI = F->arg_begin(); for (unsigned Idx = 0, e = Args.size(); Idx != e; ++Idx, ++AI) { // Create an alloca for this variable. AllocaInst *Alloca = CreateEntryBlockAlloca(F, Args[Idx]); // Store the initial value into the alloca. Builder.CreateStore(AI, Alloca); // Add arguments to variable symbol table. NamedValues[Args[Idx]] = Alloca; } } Function *FunctionAST::Codegen() { NamedValues.clear(); Function *TheFunction = Proto->Codegen(); if (TheFunction == 0) return 0; // If this is an operator, install it. if (Proto->isBinaryOp()) BinopPrecedence[Proto->getOperatorName()] = Proto->getBinaryPrecedence(); // Create a new basic block to start insertion into. BasicBlock *BB = BasicBlock::Create(getGlobalContext(), "entry", TheFunction); Builder.SetInsertPoint(BB); // Add all arguments to the symbol table and create their allocas. Proto->CreateArgumentAllocas(TheFunction); if (Value *RetVal = Body->Codegen()) { // Finish off the function. Builder.CreateRet(RetVal); // Validate the generated code, checking for consistency. verifyFunction(*TheFunction); // Optimize the function. TheFPM->run(*TheFunction); return TheFunction; } // Error reading body, remove function. TheFunction->eraseFromParent(); if (Proto->isBinaryOp()) BinopPrecedence.erase(Proto->getOperatorName()); return 0; } //===----------------------------------------------------------------------===// // Top-Level parsing and JIT Driver //===----------------------------------------------------------------------===// static ExecutionEngine *TheExecutionEngine; static void HandleDefinition() { if (FunctionAST *F = ParseDefinition()) { if (Function *LF = F->Codegen()) { fprintf(stderr, "Read function definition:"); LF->dump(); } } else { // Skip token for error recovery. getNextToken(); } } static void HandleExtern() { if (PrototypeAST *P = ParseExtern()) { if (Function *F = P->Codegen()) { fprintf(stderr, "Read extern: "); F->dump(); } } else { // Skip token for error recovery. getNextToken(); } } static void HandleTopLevelExpression() { // Evaluate a top-level expression into an anonymous function. if (FunctionAST *F = ParseTopLevelExpr()) { if (Function *LF = F->Codegen()) { // JIT the function, returning a function pointer. void *FPtr = TheExecutionEngine->getPointerToFunction(LF); // Cast it to the right type (takes no arguments, returns a double) so we // can call it as a native function. double (*FP)() = (double (*)())(intptr_t)FPtr; fprintf(stderr, "Evaluated to %f\n", FP()); } } else { // Skip token for error recovery. getNextToken(); } } /// top ::= definition | external | expression | ';' static void MainLoop() { while (1) { fprintf(stderr, "ready> "); switch (CurTok) { case tok_eof: return; case ';': getNextToken(); break; // ignore top-level semicolons. case tok_def: HandleDefinition(); break; case tok_extern: HandleExtern(); break; default: HandleTopLevelExpression(); break; } } } //===----------------------------------------------------------------------===// // "Library" functions that can be "extern'd" from user code. //===----------------------------------------------------------------------===// /// putchard - putchar that takes a double and returns 0. extern "C" double putchard(double X) { putchar((char)X); return 0; } /// printd - printf that takes a double prints it as "%f\n", returning 0. extern "C" double printd(double X) { printf("%f\n", X); return 0; } //===----------------------------------------------------------------------===// // Main driver code. //===----------------------------------------------------------------------===// int main() { InitializeNativeTarget(); LLVMContext &Context = getGlobalContext(); // Install standard binary operators. // 1 is lowest precedence. BinopPrecedence['='] = 2; BinopPrecedence['<'] = 10; BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20; BinopPrecedence['*'] = 40; // highest. // Prime the first token. fprintf(stderr, "ready> "); getNextToken(); // Make the module, which holds all the code. TheModule = new Module("my cool jit", Context); // Create the JIT. This takes ownership of the module. std::string ErrStr; TheExecutionEngine = EngineBuilder(TheModule).setErrorStr(&ErrStr).create(); if (!TheExecutionEngine) { fprintf(stderr, "Could not create ExecutionEngine: %s\n", ErrStr.c_str()); exit(1); } FunctionPassManager OurFPM(TheModule); // Set up the optimizer pipeline. Start with registering info about how the // target lays out data structures. OurFPM.add(new TargetData(*TheExecutionEngine->getTargetData())); // Provide basic AliasAnalysis support for GVN. OurFPM.add(createBasicAliasAnalysisPass()); // Promote allocas to registers. OurFPM.add(createPromoteMemoryToRegisterPass()); // Do simple "peephole" optimizations and bit-twiddling optzns. OurFPM.add(createInstructionCombiningPass()); // Reassociate expressions. OurFPM.add(createReassociatePass()); // Eliminate Common SubExpressions. OurFPM.add(createGVNPass()); // Simplify the control flow graph (deleting unreachable blocks, etc). OurFPM.add(createCFGSimplificationPass()); OurFPM.doInitialization(); // Set the global so the code gen can use this. TheFPM = &OurFPM; // Run the main "interpreter loop" now. MainLoop(); TheFPM = 0; // Print out all of the generated code. TheModule->dump(); return 0; }


Renee Young Reacts To Jon Moxley's Bloody Match Vs. Kenny Omega At AEW 'Full Gear'
by Tom ClarkJon Moxley and Kenny Omega waged a bloody and brutal war in their Lights Out Match at AEW Full Gear on Saturday, November 9. The two men fought throughout the arena, utilizing a variety weapons in a hardcore contest that fans continued to buzz about the day afterward.
Not surprisingly, the fans aren’t the only ones who are reacting to the match, as Jon Moxley’s wife also had something to say. Renee Young commented a few different times on her Twitter account and the WWE commentator was not thrilled with what she was seeing.
Many viewers shared her opinion, as they witnessed more of a bloodbath than an actual bout. Mox and Omega incorporated barbed wire, which left them both a blood mess by the time the match ended. Fans in the building cringed as they saw Omega drag the barbed wire broom across Moxley’s back as it became apparent that they were going for the throat in terms of fan reaction.
Moxley did promise the world in a promo on Dynamite that they would see “beautiful glorious wrestling violence, the likes of which you haven’t seen in this country in decades.” It’s safe to say that Mox came through on that promise and then some.
Even though Young’s Tweets were lighthearted in nature, they do raise some interesting questions that AEW fans are now asking themselves. Was this match really necessary? Does Tony Khan’c company, which is fighting to distinguish itself in the crowded pro wrestling marketplace, need to go this far in order to make an impact?
Photo: AEW
If the intention was to deliver something controversial that would draw attention to AEW’s product, then it’s mission accomplished. Whether or not it was the right kind of attention that will serve AEW well moving forward, is still being debated.


Username:{{report.login}}
Repositories:{{report.public_repos}}
Followers: {{report.followers}}
Following: {{report.following}}
Bio : {{report.bio}}
{{report.html_url}} Visit Repositories


min
this is the beta page. check minfwk.com to see the latest stable release.
btn-a
btn-b
btn-c
btn-small
h1 title
h1
h2
h3
h4
h5
h6
$
Min Framework
One
Two
Three
#
Widgets Sold
Revenue (£)
Profit (£)
1
5
10
2
2
10
20
4
3
500
1000
200
Watch out!
You don't have H9RBS.js installed.
Great!
You successfully frobjugated the minlingator's snorbdralob.
Hey!
You've got four thousand new grabjalognibs!
✉ ✰ ☁ ⌚ ⌘ ☎ ✈ ☑ ☒
♂ ♀ ⌛ ☀ ☮ ☢ ☠ ☣ ⌨
ⓧ © § ® ⌂ ℗ ☺ ☻ ☼
⇦ ⇧ ⇨ ⇩ ∡ ∿ ⊝ ⊘ ⌂
♠ ♣ ♥ ♦ ♪ ♫ ♭ ♮ ♯
10
9
1
8
2
7
3
6
4
5
5
4
6
3
7
2
8
1
9
10
Download


And at day's end,
Laying on my back.
Lorem ipsum dolor sit amet, graece nusquam eos te. Has viris platonem oportere an, cu aeterno gloriatur sea. Eos ad malis vocent voluptatibus, et habeo recusabo pertinacia mel. Vix in veri nusquam accusamus, ne vel quas percipit vituperata, posse docendi salutatus no vel.


Blizzard UX Test.
01
context
"The Blizzard Mobile team is partnering with the game team developing World of Warcraft. The World of Warcraft Armory app was originally launched in July 2009 and the time has come to give the app renewed focus and modernize the experience. As a player favorite, we have decided to release an Auction House focused app that offers a delightful and streamlined version of the current experience.Deliver a design brief that includes your research and approach supported by interaction flows or a design prototype that demonstrates the newly streamlined core functions of the new Auction House app. You may choose to focus on the iOS or Android version."
- Outline necessary research and design process
- Provide UX deliverables to communicate your research and solution
- You may include other artifacts such as personas, visual designs or working sketches to support your decisions but they are not required
- Describe what methods you would ideally use to validate your design decisions.
Goal: Design a solution to tackle WoW's complex Auction House, that streamlines from the existing app, while creating a newer, more modern experience.
02
my role
As this was an individual design test, I took in hand all parts of the designing, research, and decision making.03
research
background research
As I am not a big World of Warcraft player (in fact, I only have vague memories playing the free trial of the game up till level 20 in middle school), I decided I needed a better understanding of the game, the community, and what role the Auction House plays throughout it all. WoW is a very complex game, with many years of history and changes. Everything ideated and created throughout this process can only be surface level due to the time and scope of the project.
I began my research by downloading and playing the game. Unfortunately, a free account doesn't allow the user to explore much of the Auction House, but I continued my exploration via the web-based Auction House, the existing mobile Auction House, and third party sites that players used for the Auction House. I spent a good amount of time familiarizing myself with the existing platforms. I wanted to note Auction House flow, user interaction animations, and design standardization.
What is the Auction House?
- A portal that allows players to buy and sell from other players
- Items range from weapons, armor, recipes, drops, etc.
- Serves as an important money-sink in the game - a game mechanic that helps control the inflation rate and in-game economy, allowing devs to introduce money and remove excess money from the economy as necessary
- Each faction (2) has its own AH, and they share a neutral one
- Alliance-linked and Horde-linked Auction Houses now exist as well
- Each capital city has its own AH
- Remote AHs exist on the web platform and the Armory app; Remote AHs limit transactions to 200 per day
- Auction Houses serve as a connection portal for players to sell and buy goods from various different platforms to various audiences
Standardization of the look and feel
- Standardization of tabs: The set, present 4 tabs are Buy, Sell, Bids, and Auction. In-game seems to have 3 set tabs (Browse, Bids, and Auctions), with some variation in other tabs.
- Login: Standardizing with Battle.net (overall Blizzard look - blue, dark, clean) vs. WoW look
- Animation: Page turning of the existing Armory app
- Search and Display: Ensuring information that is displayed for each item is standardized, as well as the sort/search/filter options.
Questions
- What is the distinction between updating the look and feel from something that is too outdated versus updating it to the point where it is beyond the look and feel of WoW? From a third party perspective, it seems as there is a fine line between updating the mobile app or creating a new app that is WoW related to be very sleek and modern (design-wise), while still keeping it in the realms of World Of Warcraft's game feel, which gives a feel of worn, grunge, and fantasy.
User feedback on current auction house
- Bid pages filled with single item posts, forcing users to scroll through hundreds of pages looking for stacks. This results in slow servers, long wait times, and encourages botting.
- Users want option to filter auction listings for buyout only.
- Users want the option to sell/buy certain number of items within a stack. In other words, perhaps scrapping stacks altogether, and just post number of availablity for a certain item, then buyers can buy however many they want of that total number. Leftover items remain on the market.
- Users rely on add-ons for the auction house to increase accuracy and speed of buyout items.
User Personas
04
value prop
Before defining features, I want to return to the given prompt, background information, and goals for this app.- Give the app renewed focus, with a modernized experience. Improved flow and intuitiveness, imrpoved visual design. Simplicity and user-centricity is key.
- Delightful and streamlined version of the current experience. Features should remain consistent with existing auction houses (across platforms), with improvements that would eventually need to reach all platforms. Look and feel should match WoW's existing design standards.
Scope and Constraints
- Lack of experience with the game.
- Lack of access to the full game features.
- The scope is unclear due to my lack of full knowledge. In addition, there is haziness in a modernized design vs. maintaining the look and feel of WoW's less modern look. There is also haziness in extent of the redesign - for example, customer support, advanced and new features, etc.
- What I will address: improve flow and intuitiveness, lessen steps needed to complete certain actions
- What I will not address: altering core features, visual design
05
features
06
Wrapping Up
Validating Design Decisions
If given the opportunity to, what would be the steps I would take to validate my design decisions?
- Cross-department collaboration: [Before designing] Designers, engineers, support, etc. all have different exposure and insight to the product and the problem at hand. A brainstorming session allows many ideas to be brought to the table and a better determination on what is important and what is not.
- Concept testing: [Before prototyping] Validate my positioning on the problem. Do players want a separate auction house app? Do players find value or disvalue of having a new auction platform?
- Usability testing: [Throughout prototyping] Ensure design process is on the right track, tackling the right pain points, creating the most efficiency and intuitiveness for the user.
- Metric and Analytics: [After implementation] Identifying KPIs and metrics to track before and after implemention. Tracking these indicators will allow the designers to recognize points of success and points to improve. For example, metrics may include average user clicks to reach goal in the auction house, number of complaints and support issues filed for the auction house, speed of item processing in the auction house, server speed of the auction house, etc.
Takeaways and Future Thoughts
- Unfortunately, given the time and resource constraints, I was not able to delve as deep as I wanted to in mockups. However, I believe research proved more valuable in this situation. Given more time, I would love to further hash out the remaining screens.
- For future iterations, I would like to explore more of the logic behind Blizzard's current Auction House system, collaborating with WoW's game team, as well as the mobile design team. I believe the haziness of the scope, as aforementioned, is a big constraint.
- With more time, I would also like to better explore and understand possible edge cases of users.
- To expand on this project, I would also love to gain more insight on the "whys" behind the feature, as well as the numbers and research that may currently exist for similar features. Siilarly, it would be very important to iterate and collaborate with existing UX designers on the Blizzard Design / WoW team.
- An important takeaway of this assignment was the value of research. Jumping straight into the assignment, I felt lost and overwhelmed by a product I was not extremely familiar with. However, with some time Googling and using the product, I began to better understand not only the product itself, but also the community of the product, which was critical to this assignment.
- My initial thoughts entering the assignment was the sheer complexity of the problem. World of Warcraft is a complex game with many years of changes and updates. There is a certain level of depth of understanding that may need to be reached before being able to properly design for this type of game. My takeaway is the value of breaking down the problem into smaller pieces. What exactly is Blizzard asking for - improved flow and modernized experience. What are the features of the existing platforms - mobile, web, in-game.
References
http://kotaku.com/5463479/blizzard-takes-the-world-of-warcraft-auction-house-out-of-gamehttp://wowwiki.wikia.com/wiki/Auction_House
https://www.engadget.com/2012/02/21/5-not-so-simple-ways-blizzard-can-fix-the-world-of-warcraft-auct/
http://coldsgoldfactory.blogspot.com/2012/08/4-tips-to-improve-your-remote-auction.html
https://worldofwarcraft.com/en-us/game/new-players-guide/part-three
Thank you for reading, Blizzard Design team!


{% extends "theme_bootstrap/base.html" %}
{% comment %}
Derived from pinax_theme_bootstrap's "theme_bootstrap/base.html".
Pinax Bootstrap Theme
Copyright (c) 2015 James Tauber and contributors.
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.
 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.
{% endcomment %}
{% block topbar_base %}


John Mylopoulos, Lawrence Chung, Eric Yu
Linked to iStar.
Requirements Engineering
"[Object Oriented Analysis] adopts ideas from object-oriented programming and blends them with ideas from semantic data modeling and knowledge representation (notably semantic networks) into a aspects of a software system. Today, there are dozens of like-minded techniques and commercial tools modeling framework that is more powerful founded on the OO way of thinking that support than traditional techniques such as data flow diagrams, structured analysis, and the like."
"The Object-Oriented Systems Analysis (OOSA) technique [12] adopts the Entity-Relationship (ER) model to capture the declarative aspects of a software system."


Kevin van Zonneveld
on 2009-01-06 14:01:10
@ Brett Zamir: Nice dude :)
Brett Zamir
on 2009-01-01 03:50:27
By the way, I noticed now that there is a built-in way in JavaScript 1.7 to do the each() iteration: using Iterator() (and otherwise progress through arrays/objects). See https://developer.mozilla.org/en/New_in_JavaScript_1.7#Looping_across_objects and https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators#Iterators
Still, it shouldn't hurt to have them with the PHP equivalents (I think the PHP way is easier)...
Brett
Brett Zamir
on 2009-01-01 03:28:57
Hi Kevin,
I think I got it working now...
Thanks for the offer... Although I don't drink (I'm a Baha'i), if I did drink, I'd be glad to have one with you... You seem like a most gracious person... Have a happy new year... And Onno too--thanks for keeping me on my toes, you guys... :)
function pos (arr) { // Alias of current
return current(arr);
}
function current (arr) {
if (!window.php_js) window.php_js = {pointers:[]};
var pointers = window.php_js.pointers;
if (pointers.indexOf(arr) === -1) {
pointers.push(arr, 0);
}
var arrpos = pointers.indexOf(arr);
var cursor = pointers[arrpos+1];
if (arr instanceof Array) {
return arr[cursor] || false;
}
var ct = 0;
for (var k in arr) {
if (ct === cursor) {
return arr[k];
}
ct++;
}
return false; // Empty
}
function next (arr) {
if (!window.php_js) window.php_js = {pointers:[]};
var pointers = window.php_js.pointers;
if (pointers.indexOf(arr) === -1) {
pointers.push(arr, 0);
}
var arrpos = pointers.indexOf(arr);
var cursor = pointers[arrpos+1];
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (ct === cursor+1) {
pointers[arrpos+1] += 1;
return arr[k];
}
ct++;
}
return false; // End
}
if (arr.length === 0 || cursor === (arr.length-1)) {
return false;
}
pointers[arrpos+1] += 1;
return arr[pointers[arrpos+1]];
}
function prev (arr) {
if (!window.php_js) window.php_js = {pointers:[]};
var pointers = window.php_js.pointers;
var arrpos = pointers.indexOf(arr);
var cursor = pointers[arrpos+1];
if (pointers.indexOf(arr) === -1 || cursor === 0) {
return false;
}
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (ct === cursor-1) {
pointers[arrpos+1] -= 1;
return arr[k];
}
ct++;
}
// Shouldn't reach here
}
if (arr.length === 0) {
return false;
}
pointers[arrpos+1] -= 1;
return arr[pointers[arrpos+1]];
}
function reset (arr) {
if (!window.php_js) window.php_js = {pointers:[]};
var pointers = window.php_js.pointers;
if (pointers.indexOf(arr) === -1) {
pointers.push(arr, 0);
}
var arrpos = pointers.indexOf(arr);
if (!(arr instanceof Array)) {
for (var k in arr) {
if (pointers.indexOf(arr) === -1) {
pointers.push(arr, 0);
}
else {
pointers[arrpos+1] = 0;
}
return arr[k];
}
return false; // Empty
}
if (arr.length === 0) {
return false;
}
pointers[arrpos+1] = 0;
return arr[pointers[arrpos+1]];
}
function end (arr) {
if (!window.php_js) window.php_js = {pointers:[]};
var pointers = window.php_js.pointers;
if (pointers.indexOf(arr) === -1) {
pointers.push(arr, 0);
}
var arrpos = pointers.indexOf(arr);
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
ct++;
var val = arr[k];
}
if (ct === 0) {
return false; // Empty
}
pointers[arrpos+1] = ct - 1;
return val;
}
if (arr.length === 0) {
return false;
}
pointers[arrpos+1] = arr.length - 1;
return arr[pointers[arrpos+1]];
}
function key (arr) {
if (!window.php_js) window.php_js = {pointers:[]};
var pointers = window.php_js.pointers;
if (pointers.indexOf(arr) === -1) {
pointers.push(arr, 0);
}
var cursor = pointers[pointers.indexOf(arr)+1];
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (ct === cursor) {
return k;
}
ct++;
}
return false; // Empty
}
if (arr.length === 0) {
return false;
}
return cursor;
}
function each (arr) {
// Will return a 4-item object unless a class property 'returnArrayOnly' is set to true on this function if want to only receive a two-item numerically-indexed array (for the sake of array destructuring in JavaScript 1.7+ (similar to list() in PHP, but as PHP does it automatically in that context and JavaScript cannot, we needed something to allow that option)
// See https://developer.mozilla.org/en/New_in_JavaScript_1.7#Destructuring_assignment
if (!window.php_js) window.php_js = {pointers:[]};
var pointers = window.php_js.pointers;
if (pointers.indexOf(arr) === -1) {
pointers.push(arr, 0);
}
var arrpos = pointers.indexOf(arr);
var cursor = pointers[arrpos+1];
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (ct === cursor) {
pointers[arrpos+1] += 1;
if (each.returnArrayOnly) {
return [k, arr[k]];
}
else {
return {1:arr[k], value:arr[k], 0:k, key:k};
}
}
ct++;
}
return false; // Empty
}
if (arr.length === 0 || cursor === arr.length) {
return false;
}
pos = cursor;
pointers[arrpos+1] += 1;
if (each.returnArrayOnly) {
return [pos, arr[pos]];
}
else {
return {1:arr[pos], value:arr[pos], 0:pos, key:pos};
}
}
Kevin van Zonneveld
on 2008-12-31 13:19:04
@ Brett Zamir: You're onto something there! I'm sure we'll work it out next year ;) Guys, best wishes and have a good one tonight. Have a drink from me will you?
And let 2009 be a happy & successful year for you & your projects :) Regards!
Brett Zamir
on 2008-12-30 18:19:10
Yeah, that's a good point about serialization.
Since we can't get unique memory addresses directly in JavaScript (thank heavens!) (and toSource() as an id would not distinguish between arrays with the same values), I think what we might try is to store their references directly (for the sake of later comparison) like this along with the key:
var pointers = window.php_js.pointers;
if (pointers.indexOf(arr) === -1) {
pointers.push(arr, incrementVal);
}
else {
pointers[pointers.indexOf(arr)+1] += incrementVal; // Or do whatever needed to be done
}
I'll try to look into that later (unless you're so inspired), as well as get back to you on the constants function... All the best...
Kevin van Zonneveld
on 2008-12-30 12:05:49
@ Brett Zamir: That's some hard labour there Brett. And it makes me smile that with these functions you could actually do this:
reset($fruit);
while ([$key, $val] = each($fruit)) {
echo ($key +":"+ $val+"\n");
}
In JavaScript :) So kuddo's to you man.
But (sorry, there always is a but) before adding, I do not feel comfortable storing the pointer inside the userobject. This may introduce the infamous Heisenbug.
Imagine you have an important object that stores user information. You work on it using reset & each (you do not expect these to alter your data in any way), serialize it, and finally send it to the server.
Now the serialized information will contain a strange element: '_phpjs_pos' that may lead to all kind of unexpected results. It may cause unaware developers some hard time debugging, and may (in some rare cases but still) even lead to server side data corruption. In short, this is not something I can willingly introduce to the project.
I'd rather it be stored in the window.php_js global variable, as we already have such a mechanism for include_once. So we're not introducing additional dirt.
I imagine something like:
window.php_js.pointers[pointerId] = key;
One challenge of that approach is that we'd have to figure out a way to add the scope to the so-called pointerId. But if we cannot overcome that I have some strong issues with adding these current implementations.
Can you guys share this opinion?
Brett Zamir
on 2008-12-20 17:59:02
While the above functioning works fine from what I can tell (besides not offering to return a regular array in circumstances as is useful with destructuring assignment in JavaScript 1.7), in order to maintain pointer state in a way which would work across all such functions (and also make the array key a little more "namespaced"), I rewrote each() as well as added all of the following functions. I hadn't thought about using a property on the array to preserve the pointer position until now, so I was mistaken in thinking it not possible to do this in JavaScript.
...Examples are adapted from the PHP manual...Note that the pointer property is not reset by other JavaScript actions as it might be in PHP (e.g., assigning an array to another variable).
$foo = {"Robert" : "Bob", "Seppo" : "Sepi"};
$bar = each($foo);
var_dump($bar)
$fruit = {'a' : 'apple', 'b' : 'banana', 'c' : 'cranberry'};
reset($fruit);
while ([$key, $val] = each($fruit)) {
alert( $key +":"+ $val+"\n");
}
$transport = new Array('foot', 'bike', 'car', 'plane');
// $transport = {a:2, b:3}; // Tried also with this
$mode = current($transport); // $mode = 'foot';
$mode = next($transport); // $mode = 'bike';
$mode = current($transport); // $mode = 'bike';
$mode = prev($transport); // $mode = 'foot';
$mode = end($transport); // $mode = 'plane';
$mode = current($transport); // $mode = 'plane';
$arr = [];
var_dump(current($arr)); // bool(false)
$arr = new Array([]);
var_dump(current($arr)); // array(0) { }
function pos (arr) { // Alias of current
return current(arr);
}
function current (arr) {
if (!arr._phpjs_pos) {
arr._phpjs_pos = 0;
}
if (arr instanceof Array) {
return arr[arr._phpjs_pos] || false;
}
var ct = 0;
for (var k in arr) {
if (k !== '_phpjs_pos') {
if (ct === arr._phpjs_pos) {
return arr[k];
}
ct++;
}
}
return false; // Empty
}
function next (arr) {
if (!arr._phpjs_pos) {
arr._phpjs_pos = 0;
}
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (k !== '_phpjs_pos') {
if (ct === arr._phpjs_pos+1) {
arr._phpjs_pos += 1;
return arr[k];
}
ct++;
}
}
return false; // End
}
if (arr.length === 0 || arr._phpjs_pos === (arr.length-1)) {
return false;
}
arr._phpjs_pos += 1;
return arr[arr._phpjs_pos];
}
function prev (arr) {
if (arr._phpjs_pos === 0 || arr._phpjs_pos === undefined) {
return false;
}
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (k !== '_phpjs_pos') {
if (ct === arr._phpjs_pos-1) {
arr._phpjs_pos -= 1;
return arr[k];
}
ct++;
}
}
// Shouldn't reach here
}
if (arr.length === 0) {
return false;
}
arr._phpjs_pos -= 1;
return arr[arr._phpjs_pos];
}
function reset (arr) {
if (!(arr instanceof Array)) {
for (var k in arr) {
if (k !== '_phpjs_pos') {
arr._phpjs_pos = 0;
return arr[k];
}
}
return false; // Empty
}
if (arr.length === 0) {
return false;
}
arr._phpjs_pos = 0;
return arr[arr._phpjs_pos];
}
function end (arr) {
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (k !== '_phpjs_pos') {
ct++;
var val = arr[k];
}
}
if (ct === 0) {
return false; // Empty
}
arr._phpjs_pos = ct - 1;
return val;
}
if (arr.length === 0) {
return false;
}
arr._phpjs_pos = arr.length - 1;
return arr[arr._phpjs_pos];
}
function key (arr) {
if (!arr._phpjs_pos) {
arr._phpjs_pos = 0;
}
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (k !== '_phpjs_pos') {
if (ct === arr._phpjs_pos) {
return k;
}
ct++;
}
}
return false; // Empty
}
if (arr.length === 0) { // Todo: verify this is result (and return false just above)
return false;
}
return arr._phpjs_pos !== undefined ? arr._phpjs_pos : 0;
}
function each (arr) {
// Will return a 4-item object unless a class property 'returnArrayOnly' is set to true on this function if want to only receive a two-item numerically-indexed array (for the sake of array destructuring in JavaScript 1.7+ (similar to list() in PHP, but as PHP does it automatically in that context and JavaScript cannot, we needed something to allow that option)
// See https://developer.mozilla.org/en/New_in_JavaScript_1.7#Destructuring_assignment
if (!arr._phpjs_pos) {
arr._phpjs_pos = 0;
}
if (!(arr instanceof Array)) {
var ct = 0;
for (var k in arr) {
if (k !== '_phpjs_pos') {
if (ct === arr._phpjs_pos) {
arr._phpjs_pos += 1;
if (each.returnArrayOnly) {
return [k, arr[k]];
}
else {
return {1:arr[k], value:arr[k], 0:k, key:k};
}
}
ct++;
}
}
return false; // Empty
}
if (arr.length === 0 || arr._phpjs_pos === arr.length) {
return false;
}
pos = arr._phpjs_pos;
arr._phpjs_pos += 1;
if (each.returnArrayOnly) {
return [pos, arr[pos]];
}
else {
return {1:arr[pos], value:arr[pos], 0:pos, key:pos};
}
}


| Module::Runtime - runtime module handling |
Module::Runtime - runtime module handling
use Module::Runtime qw( $module_name_rx is_module_name check_module_name module_notional_filename require_module );
if($module_name =~ /\A$module_name_rx\z/o) { ... if(is_module_name($module_name)) { ... check_module_name($module_name);
$notional_filename = module_notional_filename($module_name); require_module($module_name);
use Module::Runtime qw(use_module use_package_optimistically);
$bi = use_module("Math::BigInt", 1.31)->new("1_234"); $widget = use_package_optimistically("Local::Widget")->new;
use Module::Runtime qw( $top_module_spec_rx $sub_module_spec_rx is_module_spec check_module_spec compose_module_name );
if($spec =~ /\A$top_module_spec_rx\z/o) { ... if($spec =~ /\A$sub_module_spec_rx\z/o) { ... if(is_module_spec("Standard::Prefix", $spec)) { ... check_module_spec("Standard::Prefix", $spec);
$module_name = compose_module_name("Standard::Prefix", $spec);
The functions exported by this module deal with runtime handling of Perl modules, which are normally handled at compile time. This module avoids using any other modules, so that it can be used in low-level infrastructure.
The parts of this module that work with module names apply the same syntax that is used for barewords in Perl source. In principle this syntax can vary between versions of Perl, and this module applies the syntax of the Perl on which it is running. In practice the usable syntax hasn't changed yet. There's some intent for Unicode module names to be supported in the future, but this hasn't yet amounted to any consistent facility.
The functions of this module whose purpose is to load modules include
workarounds for three old Perl core bugs regarding require
. These
workarounds are applied on any Perl version where the bugs exist, except
for a case where one of the bugs cannot be adequately worked around in
pure Perl.
The usable module name syntax has not changed from Perl 5.000 up to Perl 5.19.8. The syntax is composed entirely of ASCII characters. From Perl 5.6 onwards there has been some attempt to allow the use of non-ASCII Unicode characters in Perl source, but it was fundamentally broken (like the entirety of Perl 5.6's Unicode handling) and remained pretty much entirely unusable until it got some attention in the Perl 5.15 series. Although Unicode is now consistently accepted by the parser in some places, it remains broken for module names. Furthermore, there has not yet been any work on how to map Unicode module names into filenames, so in that respect also Unicode module names are unusable.
The module name syntax is, precisely: the string must consist of one or
more segments separated by ::
; each segment must consist of one or more
identifier characters (ASCII alphanumerics plus "_"); the first character
of the string must not be a digit. Thus "IO::File
", "warnings
",
and "foo::123::x_0
" are all valid module names, whereas "IO::
"
and "1foo::bar
" are not. '
separators are not permitted by this
module, though they remain usable in Perl source, being translated to
::
in the parser.
The first bug worked around is core bug [perl #68590], which causes
lexical state in one file to leak into another that is require
d/use
d
from it. This bug is present from Perl 5.6 up to Perl 5.10, and is
fixed in Perl 5.11.0. From Perl 5.9.4 up to Perl 5.10.0 no satisfactory
workaround is possible in pure Perl. The workaround means that modules
loaded via this module don't suffer this pollution of their lexical
state. Modules loaded in other ways, or via this module on the Perl
versions where the pure Perl workaround is impossible, remain vulnerable.
The module the Lexical::SealRequireHints manpage provides a complete workaround
for this bug.
The second bug worked around causes some kinds of failure in module
loading, principally compilation errors in the loaded module, to be
recorded in %INC
as if they were successful, so later attempts to load
the same module immediately indicate success. This bug is present up
to Perl 5.8.9, and is fixed in Perl 5.9.0. The workaround means that a
compilation error in a module loaded via this module won't be cached as
a success. Modules loaded in other ways remain liable to produce bogus
%INC
entries, and if a bogus entry exists then it will mislead this
module if it is used to re-attempt loading.
The third bug worked around causes the wrong context to be seen at
file scope of a loaded module, if require
is invoked in a location
that inherits context from a higher scope. This bug is present up to
Perl 5.11.2, and is fixed in Perl 5.11.3. The workaround means that
a module loaded via this module will always see the correct context.
Modules loaded in other ways remain vulnerable.
These regular expressions do not include any anchors, so to check whether an entire string matches a syntax item you must supply the anchors yourself.
Matches a valid Perl module name in bareword syntax.
Matches a module specification for use with compose_module_name, where no prefix is being used.
Matches a module specification for use with compose_module_name, where a prefix is being used.
Returns a truth value indicating whether ARG is a plain string satisfying Perl module name syntax as described for $module_name_rx.
Deprecated alias for is_module_name.
Check whether ARG is a plain string
satisfying Perl module name syntax as described for $module_name_rx.
Return normally if it is, or die
if it is not.
Generates a notional relative filename for a module, which is used in
some Perl core interfaces.
The NAME is a string, which should be a valid module name (one or
more ::
-separated segments). If it is not a valid name, the function
die
s.
The notional filename for the named module is generated and returned.
This filename is always in Unix style, with /
directory separators
and a .pm
suffix. This kind of filename can be used as an argument to
require
, and is the key that appears in %INC
to identify a module,
regardless of actual local filename syntax.
This is essentially the bareword form of require
, in runtime form.
The NAME is a string, which should be a valid module name (one or
more ::
-separated segments). If it is not a valid name, the function
die
s.
The module specified by NAME is loaded, if it hasn't been already,
in the manner of the bareword form of require
. That means that a
search through @INC
is performed, and a byte-compiled form of the
module will be used if available.
The return value is as for require
. That is, it is the value returned
by the module itself if the module is loaded anew, or 1
if the module
was already loaded.
This is essentially use
in runtime form, but without the importing
feature (which is fundamentally a compile-time thing). The NAME is
handled just like in require_module
above: it must be a module name,
and the named module is loaded as if by the bareword form of require
.
If a VERSION is specified, the VERSION
method of the loaded module is
called with the specified VERSION as an argument. This normally serves to
ensure that the version loaded is at least the version required. This is
the same functionality provided by the VERSION parameter of use
.
On success, the name of the module is returned. This is unlike require_module, and is done so that the entire call to use_module can be used as a class name to call a constructor, as in the example in the synopsis.
This is an analogue of use_module for the situation where there is uncertainty as to whether a package/class is defined in its own module or by some other means. It attempts to arrange for the named package to be available, either by loading a module or by doing nothing and hoping.
An attempt is made to load the named module (as if by the bareword form
of require
). If the module cannot be found then it is assumed that
the package was actually already loaded by other means, and no error
is signalled. That's the optimistic bit.
This is mostly the same operation that is performed by the the base manpage pragma to ensure that the specified base classes are available. The behaviour of the base manpage was simplified in version 2.18, and later improved in version 2.20, and on both occasions this function changed to match.
If a VERSION is specified, the VERSION
method of the loaded package is
called with the specified VERSION as an argument. This normally serves
to ensure that the version loaded is at least the version required.
On success, the name of the package is returned. These aspects of the
function work just like use_module.
Returns a truth value indicating whether SPEC is valid input for compose_module_name. See below for what that entails. Whether a PREFIX is supplied affects the validity of SPEC, but the exact value of the prefix is unimportant, so this function treats PREFIX as a truth value.
Deprecated alias for is_module_spec.
Check whether SPEC is valid input for compose_module_name.
Return normally if it is, or die
if it is not.
This function is intended to make it more convenient for a user to specify a Perl module name at runtime. Users have greater need for abbreviations and context-sensitivity than programmers, and Perl module names get a little unwieldy. SPEC is what the user specifies, and this function translates it into a module name in standard form, which it returns.
SPEC has syntax approximately that of a standard module name: it
should consist of one or more name segments, each of which consists
of one or more identifier characters. However, /
is permitted as a
separator, in addition to the standard ::
. The two separators are
entirely interchangeable.
Additionally, if PREFIX is not undef
then it must be a module
name in standard form, and it is prefixed to the user-specified name.
The user can inhibit the prefix addition by starting SPEC with a
separator (either /
or ::
).
On Perl versions 5.7.2 to 5.8.8, if require
is overridden by the
CORE::GLOBAL
mechanism, it is likely to break the heuristics used by
use_package_optimistically, making it signal an error for a missing
module rather than assume that it was already loaded. From Perl 5.8.9
onwards, and on 5.7.1 and earlier, this module can avoid being confused
by such an override. On the affected versions, a require
override
might be installed by the Lexical::SealRequireHints manpage, if something requires
its bugfix but for some reason its XS implementation isn't available.
the Lexical::SealRequireHints manpage, the base manpage, perlfunc/require, perlfunc/use
Andrew Main (Zefram<EMAIL_ADDRESS>Copyright (C) 2004, 2006, 2007, 2009, 2010, 2011, 2012, 2014 Andrew Main (Zefram<EMAIL_ADDRESS>This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| Module::Runtime - runtime module handling |


Our team is ready, to cater for the photo and video needs that yourself, your family, your organization needs. We are committed to providing the best service, which will suit your requirements.
Wedding is more than the just the union of two individuals. Stronger family bonds are made, friendships are valued, beliefs are affirmed. It is a joyous occassion, to cherish forever. We look into capturing the heart of the event, personalities of the bride and groom, the family, all that makes each wedding and marriage unique.
Maternity is a lifechanging journey for the couple. You develop into a stronger team, to bring a new person into this world and raise these little individuals, together as a family. Babies are little gems of happiness. That bundle of joy, whose gestures and adorable smiles, which keep changing as they grow. We would love to preserve those precious moments, that you can reminisce together in future.
Birthdays are the milestones of life. They mark the growth and life your little precious. That would be the sweet 16 or the ever important 18th! Or, be it any family occassion that needs to memorized! We got it covered!
Spend more qality time with the love of your life. Make the memories, Frame those moments, Trust us to capture the zest and spirit of your relationship and love, Book a couple photography session with us - prewedding, post wedding, anniversary, whatever the occassion be!
 It means you will love and be loved for the rest of your life.
Babies are little gems of happiness. That bundle of joy, whose gestures and adorable smiles, which keep changing as they grow. We would love to preserve those precious moments, that you can reminisce together in future.
We have expertise in covering classical and modern stage performances, solo and group perfromances, at wide variety of stages including the most prestigious venues like Esplanade, The Star Vista.
Are you looking for someone to cover the annual Dinner & Dance event? Would you like your company event covered? Would you like a trailer highlight video, which will bring the much deserved boost into your company's portfolio?
We offer videography services for a variety of domains such as to our corporate clients, concerts, dance & stage programmes, production services, family functions, music albums.
Aerial and land based services; most vendors stop at that. However, we love to take our gear underwater and dive deep to uncover the mysteries that lies beneath our waters. We routinely cover corporate events that need underwater footages along with covering recreational diving


GFA Boss Kurt Okraku inspires Black Meteors to secure Olympic ticket with last chance
GFA President Kurt E.S. Okraku has urged the Black Meteors to forgo their loss against Ivory Coast and grab a ticket to the 2020 Olympic Games in the third place play-off.
The Ghana U23 lost on penalties in the semi-finals of the U23 Africa Cup of Nations to Ivory Coast on Tuesday which blocked their chance of securing a place in next year's Olympic Games in Tokyo.
The Black Meteors have another chance to book a place in the summer Olympics as they play South Africa in the third place play-off on Friday.
A win will see Ghana booking their ticket to Tokyo.
Following Tuesday's defeat, the GFA Boss was at the team's hotel after the game to inspire and encourage the players and technical staff that all is not lost.
"This match is gone let’s focus on the next match on Friday and our next opponents. All hope is not lost." he said.
"Our ambition is to qualify to the Olympics if we fight like we did in the second half (against Ivory Coast) we will make it to the Olympics."
He implored the entire team to remain positive ahead of the third place play off.
"Let’s be positive about Friday and get the conviction that we can do it."
He also advised the team to let go all the challenges and focus on achieving their priority.
"This team has gone through battles and people have lost hope. Once again We will give hope back to the entire nation on Friday by securing a ticket to Tokyo."