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 g_named_values 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 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 will need to create the allocas that we will store in g_named_values. We'll use a helper function that ensures that the allocas are created in the entry block of the function:
# Creates an alloca instruction in the entry block of the function. This is used # for mutable variables. def CreateEntryBlockAlloca(function, var_name): entry = function.get_entry_basic_block() builder = Builder.new(entry) builder.position_at_beginning(entry) return builder.alloca(Type.double(), var_name)
This code creates a temporary llvm.core.Builder that is pointing at the first instruction 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:
def CodeGen(self): if self.name in g_named_values: return g_llvm_builder.load(g_named_values[self.name], self.name) else: raise RuntimeError('Unknown variable name: ' + self.name)
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 ForExpressionNode.CodeGen (see the full code listing for the unabridged code):
def CodeGen(self): function = g_llvm_builder.basic_block.function # Create an alloca for the variable in the entry block. alloca = CreateEntryBlockAlloca(function, self.loop_variable) # Emit the start code first, without 'variable' in scope. start_value = self.start.CodeGen() # Store the value into the alloca. g_llvm_builder.store(start_value, alloca) ... # Compute the end condition. end_condition = self.end.CodeGen() # Reload, increment, and restore the alloca. This handles the case where # the body of the loop mutates the variable. cur_value = g_llvm_builder.load(alloca, self.loop_variable) next_value = g_llvm_builder.fadd(cur_value, step_value, 'nextvar') g_llvm_builder.store(next_value, alloca) # Convert condition to a bool by comparing equal to 0.0. end_condition_bool = g_llvm_builder.fcmp( FCMP_ONE, end_condition, Constant.real(Type.double(), 0), 'loopcond') ...
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:
class PrototypeNode(object): ... # Create an alloca for each argument and register the argument in the symbol # table so that references to it will succeed. def CreateArgumentAllocas(self, function): for arg_name, arg in zip(self.args, function.args): alloca = CreateEntryBlockAlloca(function, arg_name) g_llvm_builder.store(arg, alloca) g_named_values[arg_name] = 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 FunctionNode.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:
from llvm.passes import (PASS_PROMOTE_MEMORY_TO_REGISTER, PASS_INSTRUCTION_COMBINING, PASS_REASSOCIATE, PASS_GVN, PASS_CFG_SIMPLIFICATION) ... def main(): # Set up the optimizer pipeline. Start with registering info about how the # target lays out data structures. g_llvm_pass_manager.add(g_llvm_executor.target_data) # Promote allocas to registers. g_llvm_pass_manager.add(PASS_PROMOTE_MEMORY_TO_REGISTER) # Do simple "peephole" optimizations and bit-twiddling optzns. g_llvm_pass_manager.add(PASS_INSTRUCTION_COMBINING) # Reassociate expressions. g_llvm_pass_manager.add(PASS_REASSOCIATE)
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:
def main(): ... # Install standard binary operators. # 1 is lowest possible precedence. 40 is the highest. g_binop_precedence['='] = 2 g_binop_precedence['<'] = 10 g_binop_precedence['+'] = 20 g_binop_precedence['-'] = 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:
class BinaryOperatorExpressionNode(ExpressionNode): ... def CodeGen(self): # A special case for '=' because we don't want to emit the LHS as an # expression. if self.operator == '=': # Assignment requires the LHS to be an identifier. if not isinstance(self.left, VariableExpressionNode): raise RuntimeError('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 = self.right.CodeGen() # Look up the name. variable = g_named_values[self.left.name] # Store the value and return it. g_llvm_builder.store(value, variable) return value ...
Once we have the variable, CodeGening 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. Let's 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:
... class UnaryToken(object): pass class VarToken(object): pass ... def Tokenize(string): ... elif identifier == 'unary': yield UnaryToken() elif identifier == 'var': yield VarToken() else: yield IdentifierToken(identifier)
The next step is to define the AST node that we will construct. For var/in, it looks like this:
# Expression class for var/in. class VarExpressionNode(ExpressionNode): def __init__(self, variables, body): self.variables = variables self.body = body def CodeGen(self): ...
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 variables list. 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 ::= # dentifierexpr | numberexpr | parenexpr | ifexpr | forexpr | varexpr def ParsePrimary(self): if isinstance(self.current, IdentifierToken): return self.ParseIdentifierExpr() elif isinstance(self.current, NumberToken): return self.ParseNumberExpr() elif isinstance(self.current, IfToken): return self.ParseIfExpr() elif isinstance(self.current, ForToken): return self.ParseForExpr() elif isinstance(self.current, VarToken): return self.ParseVarExpr() elif self.current == CharacterToken('('): return self.ParseParenExpr() else: raise RuntimeError('Unknown token when expecting an expression.')
Next we define ParseVarExpr:
# varexpr ::= 'var' (identifier ('=' expression)?)+ 'in' expression def ParseVarExpr(self): self.Next() # eat 'var'. variables = {} # At least one variable name is required. if not isinstance(self.current, IdentifierToken): raise RuntimeError('Expected identifier after "var".')
The first part of this code parses the list of identifier/expr pairs into the local variables list.
while True: var_name = self.current.name self.Next() # eat the identifier. # Read the optional initializer. if self.current == CharacterToken('='): self.Next() # eat '='. variables[var_name] = self.ParseExpression() else: variables[var_name] = None # End of var list, exit loop. if self.current != CharacterToken(','): break self.Next() # eat ','. if not isinstance(self.current, IdentifierToken): raise RuntimeError('Expected identifier after "," in a var expression.')
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 not isinstance(self.current, InToken): raise RuntimeError('Expected "in" keyword after "var".') self.Next() # eat 'in'. body = self.ParseExpression() return VarExpressionNode(variables, 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:
class VarExpressionNode(ExpressionNode): ... def CodeGen(self): old_bindings = {} function = g_llvm_builder.basic_block.function # Register all variables and emit their initializer. for var_name, var_expression in self.variables.iteritems(): # 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'. if var_expression is not None: var_value = var_expression.CodeGen() else: var_value = Constant.real(Type.double(), 0) alloca = CreateEntryBlockAlloca(function, var_name) g_llvm_builder.store(var_value, alloca) # Remember the old variable binding so that we can restore the binding # when we unrecurse. old_bindings[var_name] = g_named_values.get(var_name, None) # Remember this binding. g_named_values[var_name] = alloca
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 old_bindings.
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. body = self.body.CodeGen()
Finally, before returning, we restore the previous variable bindings:
# Pop all our variables from scope. for var_name in self.variables: if old_bindings[var_name] is not None: g_named_values[var_name] = old_bindings[var_name] else: del g_named_values[var_name] # Return the body computation. return body
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:
#!/usr/bin/env python import re from llvm.core import Module, Constant, Type, Function, Builder from llvm.ee import ExecutionEngine, TargetData from llvm.passes import FunctionPassManager from llvm.core import FCMP_ULT, FCMP_ONE from llvm.passes import (PASS_PROMOTE_MEMORY_TO_REGISTER, PASS_INSTRUCTION_COMBINING, PASS_REASSOCIATE, PASS_GVN, PASS_CFG_SIMPLIFICATION) ################################################################################ ## Globals ################################################################################ # The LLVM module, which holds all the IR code. g_llvm_module = Module.new('my cool jit') # The LLVM instruction builder. Created whenever a new function is entered. g_llvm_builder = None # A dictionary that keeps track of which values are defined in the current scope # and what their LLVM representation is. g_named_values = {} # The function optimization passes manager. g_llvm_pass_manager = FunctionPassManager.new(g_llvm_module) # The LLVM execution engine. g_llvm_executor = ExecutionEngine.new(g_llvm_module) # The binary operator precedence chart. g_binop_precedence = {} # Creates an alloca instruction in the entry block of the function. This is used # for mutable variables. def CreateEntryBlockAlloca(function, var_name): entry = function.get_entry_basic_block() builder = Builder.new(entry) builder.position_at_beginning(entry) return builder.alloca(Type.double(), var_name) ################################################################################ ## Lexer ################################################################################ # The lexer yields one of these types for each token. class EOFToken(object): pass class DefToken(object): pass class ExternToken(object): pass class IfToken(object): pass class ThenToken(object): pass class ElseToken(object): pass class ForToken(object): pass class InToken(object): pass class BinaryToken(object): pass class UnaryToken(object): pass class VarToken(object): pass class IdentifierToken(object): def __init__(self, name): self.name = name class NumberToken(object): def __init__(self, value): self.value = value class CharacterToken(object): def __init__(self, char): self.char = char def __eq__(self, other): return isinstance(other, CharacterToken) and self.char == other.char def __ne__(self, other): return not self == other # Regular expressions that tokens and comments of our language. REGEX_NUMBER = re.compile('[0-9]+(?:\.[0-9]+)?') REGEX_IDENTIFIER = re.compile('[a-zA-Z][a-zA-Z0-9]*') REGEX_COMMENT = re.compile('#.*') def Tokenize(string): while string: # Skip whitespace. if string[0].isspace(): string = string[1:] continue # Run regexes. comment_match = REGEX_COMMENT.match(string) number_match = REGEX_NUMBER.match(string) identifier_match = REGEX_IDENTIFIER.match(string) # Check if any of the regexes matched and yield the appropriate result. if comment_match: comment = comment_match.group(0) string = string[len(comment):] elif number_match: number = number_match.group(0) yield NumberToken(float(number)) string = string[len(number):] elif identifier_match: identifier = identifier_match.group(0) # Check if we matched a keyword. if identifier == 'def': yield DefToken() elif identifier == 'extern': yield ExternToken() elif identifier == 'if': yield IfToken() elif identifier == 'then': yield ThenToken() elif identifier == 'else': yield ElseToken() elif identifier == 'for': yield ForToken() elif identifier == 'in': yield InToken() elif identifier == 'binary': yield BinaryToken() elif identifier == 'unary': yield UnaryToken() elif identifier == 'var': yield VarToken() else: yield IdentifierToken(identifier) string = string[len(identifier):] else: # Yield the ASCII value of the unknown character. yield CharacterToken(string[0]) string = string[1:] yield EOFToken() ################################################################################ ## Abstract Syntax Tree (aka Parse Tree) ################################################################################ # Base class for all expression nodes. class ExpressionNode(object): pass # Expression class for numeric literals like "1.0". class NumberExpressionNode(ExpressionNode): def __init__(self, value): self.value = value def CodeGen(self): return Constant.real(Type.double(), self.value) # Expression class for referencing a variable, like "a". class VariableExpressionNode(ExpressionNode): def __init__(self, name): self.name = name def CodeGen(self): if self.name in g_named_values: return g_llvm_builder.load(g_named_values[self.name], self.name) else: raise RuntimeError('Unknown variable name: ' + self.name) # Expression class for a binary operator. class BinaryOperatorExpressionNode(ExpressionNode): def __init__(self, operator, left, right): self.operator = operator self.left = left self.right = right def CodeGen(self): # A special case for '=' because we don't want to emit the LHS as an # expression. if self.operator == '=': # Assignment requires the LHS to be an identifier. if not isinstance(self.left, VariableExpressionNode): raise RuntimeError('Destination of "=" must be a variable.') # Codegen the RHS. value = self.right.CodeGen() # Look up the name. variable = g_named_values[self.left.name] # Store the value and return it. g_llvm_builder.store(value, variable) return value left = self.left.CodeGen() right = self.right.CodeGen() if self.operator == '+': return g_llvm_builder.fadd(left, right, 'addtmp') elif self.operator == '-': return g_llvm_builder.fsub(left, right, 'subtmp') elif self.operator == '*': return g_llvm_builder.fmul(left, right, 'multmp') elif self.operator == '<': result = g_llvm_builder.fcmp(FCMP_ULT, left, right, 'cmptmp') # Convert bool 0 or 1 to double 0.0 or 1.0. return g_llvm_builder.uitofp(result, Type.double(), 'booltmp') else: function = g_llvm_module.get_function_named('binary' + self.operator) return g_llvm_builder.call(function, [left, right], 'binop') # Expression class for function calls. class CallExpressionNode(ExpressionNode): def __init__(self, callee, args): self.callee = callee self.args = args def CodeGen(self): # Look up the name in the global module table. callee = g_llvm_module.get_function_named(self.callee) # Check for argument mismatch error. if len(callee.args) != len(self.args): raise RuntimeError('Incorrect number of arguments passed.') arg_values = [i.CodeGen() for i in self.args] return g_llvm_builder.call(callee, arg_values, 'calltmp') # Expression class for if/then/else. class IfExpressionNode(ExpressionNode): def __init__(self, condition, then_branch, else_branch): self.condition = condition self.then_branch = then_branch self.else_branch = else_branch def CodeGen(self): condition = self.condition.CodeGen() # Convert condition to a bool by comparing equal to 0.0. condition_bool = g_llvm_builder.fcmp( FCMP_ONE, condition, Constant.real(Type.double(), 0), 'ifcond') function = g_llvm_builder.basic_block.function # Create blocks for the then and else cases. Insert the 'then' block at the # end of the function. then_block = function.append_basic_block('then') else_block = function.append_basic_block('else') merge_block = function.append_basic_block('ifcond') g_llvm_builder.cbranch(condition_bool, then_block, else_block) # Emit then value. g_llvm_builder.position_at_end(then_block) then_value = self.then_branch.CodeGen() g_llvm_builder.branch(merge_block) # Codegen of 'Then' can change the current block; update then_block for the # PHI node. then_block = g_llvm_builder.basic_block # Emit else block. g_llvm_builder.position_at_end(else_block) else_value = self.else_branch.CodeGen() g_llvm_builder.branch(merge_block) # Codegen of 'Else' can change the current block, update else_block for the # PHI node. else_block = g_llvm_builder.basic_block # Emit merge block. g_llvm_builder.position_at_end(merge_block) phi = g_llvm_builder.phi(Type.double(), 'iftmp') phi.add_incoming(then_value, then_block) phi.add_incoming(else_value, else_block) return phi # Expression class for for/in. class ForExpressionNode(ExpressionNode): def __init__(self, loop_variable, start, end, step, body): self.loop_variable = loop_variable self.start = start self.end = end self.step = step self.body = body def CodeGen(self): # 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 = g_llvm_builder.basic_block.function # Create an alloca for the variable in the entry block. alloca = CreateEntryBlockAlloca(function, self.loop_variable) # Emit the start code first, without 'variable' in scope. start_value = self.start.CodeGen() # Store the value into the alloca. g_llvm_builder.store(start_value, alloca) # Make the new basic block for the loop, inserting after current block. loop_block = function.append_basic_block('loop') # Insert an explicit fall through from the current block to the loop_block. g_llvm_builder.branch(loop_block) # Start insertion in loop_block. g_llvm_builder.position_at_end(loop_block) # Within the loop, the variable is defined equal to the alloca. If it # shadows an existing variable, we have to restore it, so save it now. old_value = g_named_values.get(self.loop_variable, None) g_named_values[self.loop_variable] = 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. self.body.CodeGen() # Emit the step value. if self.step: step_value = self.step.CodeGen() else: # If not specified, use 1.0. step_value = Constant.real(Type.double(), 1) # Compute the end condition. end_condition = self.end.CodeGen() # Reload, increment, and restore the alloca. This handles the case where # the body of the loop mutates the variable. cur_value = g_llvm_builder.load(alloca, self.loop_variable) next_value = g_llvm_builder.fadd(cur_value, step_value, 'nextvar') g_llvm_builder.store(next_value, alloca) # Convert condition to a bool by comparing equal to 0.0. end_condition_bool = g_llvm_builder.fcmp( FCMP_ONE, end_condition, Constant.real(Type.double(), 0), 'loopcond') # Create the "after loop" block and insert it. after_block = function.append_basic_block('afterloop') # Insert the conditional branch into the end of loop_block. g_llvm_builder.cbranch(end_condition_bool, loop_block, after_block) # Any new code will be inserted in after_block. g_llvm_builder.position_at_end(after_block) # Restore the unshadowed variable. if old_value is not None: g_named_values[self.loop_variable] = old_value else: del g_named_values[self.loop_variable] # for expr always returns 0.0. return Constant.real(Type.double(), 0) # Expression class for a unary operator. class UnaryExpressionNode(ExpressionNode): def __init__(self, operator, operand): self.operator = operator self.operand = operand def CodeGen(self): operand = self.operand.CodeGen() function = g_llvm_module.get_function_named('unary' + self.operator) return g_llvm_builder.call(function, [operand], 'unop') # Expression class for var/in. class VarExpressionNode(ExpressionNode): def __init__(self, variables, body): self.variables = variables self.body = body def CodeGen(self): old_bindings = {} function = g_llvm_builder.basic_block.function # Register all variables and emit their initializer. for var_name, var_expression in self.variables.iteritems(): # 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'. if var_expression is not None: var_value = var_expression.CodeGen() else: var_value = Constant.real(Type.double(), 0) alloca = CreateEntryBlockAlloca(function, var_name) g_llvm_builder.store(var_value, alloca) # Remember the old variable binding so that we can restore the binding # when we unrecurse. old_bindings[var_name] = g_named_values.get(var_name, None) # Remember this binding. g_named_values[var_name] = alloca # Codegen the body, now that all vars are in scope. body = self.body.CodeGen() # Pop all our variables from scope. for var_name in self.variables: if old_bindings[var_name] is not None: g_named_values[var_name] = old_bindings[var_name] else: del g_named_values[var_name] # Return the body computation. return body # 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 PrototypeNode(object): def __init__(self, name, args, is_operator=False, precedence=0): self.name = name self.args = args self.is_operator = is_operator self.precedence = precedence def IsBinaryOp(self): return self.is_operator and len(self.args) == 2 def GetOperatorName(self): assert self.is_operator return self.name[-1] def CodeGen(self): # Make the function type, eg. double(double,double). funct_type = Type.function( Type.double(), [Type.double()] * len(self.args), False) function = Function.new(g_llvm_module, funct_type, self.name) # If the name conflicted, there was already something with the same name. # If it has a body, don't allow redefinition or reextern. if function.name != self.name: function.delete() function = g_llvm_module.get_function_named(self.name) # If the function already has a body, reject this. if not function.is_declaration: raise RuntimeError('Redefinition of function.') # If the function took a different number of args, reject. if len(function.args) != len(self.args): raise RuntimeError('Redeclaration of a function with different number ' 'of args.') # Set names for all arguments and add them to the variables symbol table. for arg, arg_name in zip(function.args, self.args): arg.name = arg_name return function # Create an alloca for each argument and register the argument in the symbol # table so that references to it will succeed. def CreateArgumentAllocas(self, function): for arg_name, arg in zip(self.args, function.args): alloca = CreateEntryBlockAlloca(function, arg_name) g_llvm_builder.store(arg, alloca) g_named_values[arg_name] = alloca # This class represents a function definition itself. class FunctionNode(object): def __init__(self, prototype, body): self.prototype = prototype self.body = body def CodeGen(self): # Clear scope. g_named_values.clear() # Create a function object. function = self.prototype.CodeGen() # If this is a binary operator, install its precedence. if self.prototype.IsBinaryOp(): operator = self.prototype.GetOperatorName() g_binop_precedence[operator] = self.prototype.precedence # Create a new basic block to start insertion into. block = function.append_basic_block('entry') global g_llvm_builder g_llvm_builder = Builder.new(block) # Add all arguments to the symbol table and create their allocas. self.prototype.CreateArgumentAllocas(function) # Finish off the function. try: return_value = self.body.CodeGen() g_llvm_builder.ret(return_value) # Validate the generated code, checking for consistency. function.verify() # Optimize the function. g_llvm_pass_manager.run(function) except: function.delete() if self.prototype.IsBinaryOp(): del g_binop_precedence[self.prototype.GetOperatorName()] raise return function ################################################################################ ## Parser ################################################################################ class Parser(object): def __init__(self, tokens): self.tokens = tokens self.Next() # Provide a simple token buffer. Parser.current is the current token the # parser is looking at. Parser.Next() reads another token from the lexer and # updates Parser.current with its results. def Next(self): self.current = self.tokens.next() # Gets the precedence of the current token, or -1 if the token is not a binary # operator. def GetCurrentTokenPrecedence(self): if isinstance(self.current, CharacterToken): return g_binop_precedence.get(self.current.char, -1) else: return -1 # identifierexpr ::= identifier | identifier '(' expression* ')' def ParseIdentifierExpr(self): identifier_name = self.current.name self.Next() # eat identifier. if self.current != CharacterToken('('): # Simple variable reference. return VariableExpressionNode(identifier_name) # Call. self.Next() # eat '('. args = [] if self.current != CharacterToken(')'): while True: args.append(self.ParseExpression()) if self.current == CharacterToken(')'): break elif self.current != CharacterToken(','): raise RuntimeError('Expected ")" or "," in argument list.') self.Next() self.Next() # eat ')'. return CallExpressionNode(identifier_name, args) # numberexpr ::= number def ParseNumberExpr(self): result = NumberExpressionNode(self.current.value) self.Next() # consume the number. return result # parenexpr ::= '(' expression ')' def ParseParenExpr(self): self.Next() # eat '('. contents = self.ParseExpression() if self.current != CharacterToken(')'): raise RuntimeError('Expected ")".') self.Next() # eat ')'. return contents # ifexpr ::= 'if' expression 'then' expression 'else' expression def ParseIfExpr(self): self.Next() # eat the if. # condition. condition = self.ParseExpression() if not isinstance(self.current, ThenToken): raise RuntimeError('Expected "then".') self.Next() # eat the then. then_branch = self.ParseExpression() if not isinstance(self.current, ElseToken): raise RuntimeError('Expected "else".') self.Next() # eat the else. else_branch = self.ParseExpression() return IfExpressionNode(condition, then_branch, else_branch) # forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression def ParseForExpr(self): self.Next() # eat the for. if not isinstance(self.current, IdentifierToken): raise RuntimeError('Expected identifier after for.') loop_variable = self.current.name self.Next() # eat the identifier. if self.current != CharacterToken('='): raise RuntimeError('Expected "=" after for variable.') self.Next() # eat the '='. start = self.ParseExpression() if self.current != CharacterToken(','): raise RuntimeError('Expected "," after for start value.') self.Next() # eat the ','. end = self.ParseExpression() # The step value is optional. if self.current == CharacterToken(','): self.Next() # eat the ','. step = self.ParseExpression() else: step = None if not isinstance(self.current, InToken): raise RuntimeError('Expected "in" after for variable specification.') self.Next() # eat 'in'. body = self.ParseExpression() return ForExpressionNode(loop_variable, start, end, step, body) # varexpr ::= 'var' (identifier ('=' expression)?)+ 'in' expression def ParseVarExpr(self): self.Next() # eat 'var'. variables = {} # At least one variable name is required. if not isinstance(self.current, IdentifierToken): raise RuntimeError('Expected identifier after "var".') while True: var_name = self.current.name self.Next() # eat the identifier. # Read the optional initializer. if self.current == CharacterToken('='): self.Next() # eat '='. variables[var_name] = self.ParseExpression() else: variables[var_name] = None # End of var list, exit loop. if self.current != CharacterToken(','): break self.Next() # eat ','. if not isinstance(self.current, IdentifierToken): raise RuntimeError('Expected identifier after "," in a var expression.') # At this point, we have to have 'in'. if not isinstance(self.current, InToken): raise RuntimeError('Expected "in" keyword after "var".') self.Next() # eat 'in'. body = self.ParseExpression() return VarExpressionNode(variables, body) # primary ::= # dentifierexpr | numberexpr | parenexpr | ifexpr | forexpr | varexpr def ParsePrimary(self): if isinstance(self.current, IdentifierToken): return self.ParseIdentifierExpr() elif isinstance(self.current, NumberToken): return self.ParseNumberExpr() elif isinstance(self.current, IfToken): return self.ParseIfExpr() elif isinstance(self.current, ForToken): return self.ParseForExpr() elif isinstance(self.current, VarToken): return self.ParseVarExpr() elif self.current == CharacterToken('('): return self.ParseParenExpr() else: raise RuntimeError('Unknown token when expecting an expression.') # unary ::= primary | unary_operator unary def ParseUnary(self): # If the current token is not an operator, it must be a primary expression. if (not isinstance(self.current, CharacterToken) or self.current in [CharacterToken('('), CharacterToken(',')]): return self.ParsePrimary() # If this is a unary operator, read it. operator = self.current.char self.Next() # eat the operator. return UnaryExpressionNode(operator, self.ParseUnary()) # binoprhs ::= (binary_operator unary)* def ParseBinOpRHS(self, left, left_precedence): # If this is a binary operator, find its precedence. while True: precedence = self.GetCurrentTokenPrecedence() # If this is a binary operator that binds at least as tightly as the # current one, consume it; otherwise we are done. if precedence < left_precedence: return left binary_operator = self.current.char self.Next() # eat the operator. # Parse the unary expression after the binary operator. right = self.ParseUnary() # If binary_operator binds less tightly with right than the operator after # right, let the pending operator take right as its left. next_precedence = self.GetCurrentTokenPrecedence() if precedence < next_precedence: right = self.ParseBinOpRHS(right, precedence + 1) # Merge left/right. left = BinaryOperatorExpressionNode(binary_operator, left, right) # expression ::= unary binoprhs def ParseExpression(self): left = self.ParseUnary() return self.ParseBinOpRHS(left, 0) # prototype # ::= id '(' id* ')' # ::= binary LETTER number? (id, id) # ::= unary LETTER (id) def ParsePrototype(self): precedence = None if isinstance(self.current, IdentifierToken): kind = 'normal' function_name = self.current.name self.Next() # eat function name. elif isinstance(self.current, UnaryToken): kind = 'unary' self.Next() # eat 'unary'. if not isinstance(self.current, CharacterToken): raise RuntimeError('Expected an operator after "unary".') function_name = 'unary' + self.current.char self.Next() # eat the operator. elif isinstance(self.current, BinaryToken): kind = 'binary' self.Next() # eat 'binary'. if not isinstance(self.current, CharacterToken): raise RuntimeError('Expected an operator after "binary".') function_name = 'binary' + self.current.char self.Next() # eat the operator. if isinstance(self.current, NumberToken): if not 1 <= self.current.value <= 100: raise RuntimeError('Invalid precedence: must be in range [1, 100].') precedence = self.current.value self.Next() # eat the precedence. else: raise RuntimeError('Expected function name, "unary" or "binary" in ' 'prototype.') if self.current != CharacterToken('('): raise RuntimeError('Expected "(" in prototype.') self.Next() # eat '('. arg_names = [] while isinstance(self.current, IdentifierToken): arg_names.append(self.current.name) self.Next() if self.current != CharacterToken(')'): raise RuntimeError('Expected ")" in prototype.') # Success. self.Next() # eat ')'. if kind == 'unary' and len(arg_names) != 1: raise RuntimeError('Invalid number of arguments for a unary operator.') elif kind == 'binary' and len(arg_names) != 2: raise RuntimeError('Invalid number of arguments for a binary operator.') return PrototypeNode(function_name, arg_names, kind != 'normal', precedence) # definition ::= 'def' prototype expression def ParseDefinition(self): self.Next() # eat def. proto = self.ParsePrototype() body = self.ParseExpression() return FunctionNode(proto, body) # toplevelexpr ::= expression def ParseTopLevelExpr(self): proto = PrototypeNode('', []) return FunctionNode(proto, self.ParseExpression()) # external ::= 'extern' prototype def ParseExtern(self): self.Next() # eat extern. return self.ParsePrototype() # Top-Level parsing def HandleDefinition(self): self.Handle(self.ParseDefinition, 'Read a function definition:') def HandleExtern(self): self.Handle(self.ParseExtern, 'Read an extern:') def HandleTopLevelExpression(self): try: function = self.ParseTopLevelExpr().CodeGen() result = g_llvm_executor.run_function(function, []) print 'Evaluated to:', result.as_real(Type.double()) except Exception, e: raise#print 'Error:', e try: self.Next() # Skip for error recovery. except: pass def Handle(self, function, message): try: print message, function().CodeGen() except Exception, e: raise#print 'Error:', e try: self.Next() # Skip for error recovery. except: pass ################################################################################ ## Main driver code. ################################################################################ def main(): # Set up the optimizer pipeline. Start with registering info about how the # target lays out data structures. g_llvm_pass_manager.add(g_llvm_executor.target_data) # Promote allocas to registers. g_llvm_pass_manager.add(PASS_PROMOTE_MEMORY_TO_REGISTER) # Do simple "peephole" optimizations and bit-twiddling optzns. g_llvm_pass_manager.add(PASS_INSTRUCTION_COMBINING) # Reassociate expressions. g_llvm_pass_manager.add(PASS_REASSOCIATE) # Eliminate Common SubExpressions. g_llvm_pass_manager.add(PASS_GVN) # Simplify the control flow graph (deleting unreachable blocks, etc). g_llvm_pass_manager.add(PASS_CFG_SIMPLIFICATION) g_llvm_pass_manager.initialize() # Install standard binary operators. # 1 is lowest possible precedence. 40 is the highest. g_binop_precedence['='] = 2 g_binop_precedence['<'] = 10 g_binop_precedence['+'] = 20 g_binop_precedence['-'] = 20 g_binop_precedence['*'] = 40 # Run the main "interpreter loop". while True: print 'ready<', try: raw = raw_input() except KeyboardInterrupt: break parser = Parser(Tokenize(raw)) while True: # top ::= definition | external | expression | EOF if isinstance(parser.current, EOFToken): break if isinstance(parser.current, DefToken): parser.HandleDefinition() elif isinstance(parser.current, ExternToken): parser.HandleExtern() else: parser.HandleTopLevelExpression() # Print out all of the generated code. print '\n', g_llvm_module if __name__ == '__main__': main()


Webstore Compare is a web app that allows to you compare prices on Walmart and Ebay stores.
On mobile versions of the app, simply pick your store (default being Walmart), type in your keyword, and press search.
On desktop versions, results for both stores are displayed by default.
Note, results may vary and depend on each store individual search engine.
Happy Shopping!


This tool is a way to track and prioritize work:
- Drag and drop items to reprioritize and move them between boxes.
- Click on an empty cell to add a new item.
Each column is managed by a tech lead. To add work to somebody else's area, add it in the bottom "Untriaged" section. In general, the tech lead for an area is responsible for making all other changes within a column.
The following features are planned but not yet implemented:
- Search
- Bugzilla sync: when bugs are assigned or fixed, the plan is to automatically move them into the review/uplift/complete sections.
- Simultaneous update: currently if multiple people make changes simultaneously, there is no visual clue.
- Some better way to annotate/track mentoring status.
- Bugs assigned to the team which are not tracked on this system.
- Mismatches between the assigned/fixed status of a bug and its status in the system.
- Display of changes to individual items over time.
- Email alerts to tech leads about untriaged items.
- Feed of recent changes.
- Periodic email reports to managers/tech-leads/all-team about current work.
Details:
- Maintainer/operator: Benjamin Smedberg
- Github code
- Issue tracker.


"x"
"1" "SURH,YJ, LIEM,A, MILLER,EC AND MILLER,JA,7-SULFOOXYMETHYL-12-METHYLBENZ[A] ANTHRACENE IS AN ELECTROPHILIC MUTAGEN, BUTDOES NOT APPEAR TO PLAY A ROLE IN CARCINOGENESIS BY7,12-DIMETHYLBENZ[A]ANTHRACENE __ OR 7-HYDROXYMETHYL-12- METHYLBENZ[A]ANTHRACENE,CARCINOGENESIS 12(2):339-347, 1991"


KNEX-I-COMP
Step By Step
Bit Frame Top installed
The bit frame top is mounted on top of the topmost
bit frame
.
common-footer.html could not be included.


This typeface was created for the purpose of experimenting with new materials and textures doing searches for new ways to communicate and explore.
First I wet my fingers with ink, making sure that the liquid is not too thick for my fingerprints could be enough noted in the paper.
Then I had to measure the proportion for each character. I wanted to make it equivalent and make it readable at the same time with the texture being noting.
And finally I had to maintain carefully their proportions and likewise played with different shades of degradation depending on the level of ink in each piece.


No items were invoiced in this time frame
| Stock Name | Sale | Product | Purchase Cost | Tax | Profit |
|---|---|---|---|---|---|
| Polyster Fabric Product | ₹6,00,000 | ₹2,00,000 | ₹50,000 | ₹3,50,000 | ₹3,50,000 |
| Polyster Fabric Product | ₹6,00,000 | ₹2,00,000 | ₹50,000 | ₹3,50,000 | ₹3,50,000 |
Star Stock
Sale: ₹6,00,000
Purchase Cost: ₹2,00,000


Puerto Galera is at the NW tip of the province of Oriental Mindoro. It is located at the SW end of the Isla Verde Passage, about 130 kilometers south of Manila. It's a popular tourist destination with many beautiful "pocket" beaches and dive spots and stunning mountain scenery.
Be cautious if stopping on your way to Puerto Galero. A popular bad weather shelter before rounding the Cape on the NW point of Mindoro is Paluan Bay. A cruising couple were boarded by armed men and robbed at gunpoint here in February 2013 (see report adjacent). Cruisers are advised to stay away from this bay and be wary of anyone approaching your boat at night.
A new system for clearance is in place if you are traveling to Puerto Galera. Arriving yachts will only be able to enter at either Muelle Pier or Balatero Pier. A Php50 Environmental User Fee is levied on every visitor, on a per visit basis.
Yachts can contact the Yacht Club in advance for information on anchorages and port clearance.
The Immigration sub-office is above the wet markets, and is open Monday, Tuesday and Wednesday mornings.
An alternative is to take a ferry to Batangas, which takes about 75 minutes and costs about 250 pesos one way, and visit the Immigration Office there at Diversion Rd, Barangay Bolbok (Ph<PHONE_NUMBER>).
There is no Customs office here.
Last updated October 2013.
Puerto Galera Yacht Club :© Karin Stubbs
The Yacht Club maintains 20 visitors' moorings very close to the western end of Muelle Bay, next to the town. the charge is 336 pesos per day (2013). Max. length 50ft/20 tons. This includes a free water taxi service and use of the yacht club facilities.
The Yacht Club moorings lie within Puerto Galera's natural typhoon shelter and are regularly maintained in order to provide high levels of security.
Anchoring
Yachts are not allowed to anchor in Muelle Bay as this is a protected marine environment, except for the fixed moorings. Anchoring is permitted in Boquete Bay or Dalaruan Bay, but take great care when anchoring so as not to disturb the protected corals and the indigenous giant clams.
Last updated October 2013.


module Orchestrate::API::Error
Not used. But it does contain nice summary of orchestrate.io api errors.
Public Instance Methods
errors()
click to toggle source
# File api/lib/orchestrate_api/errors.rb, line 6 def errors @@errors ||= [ { :status => 400, :code => :api_bad_request, :desc => 'The API request is malformed.' }, { :status => 500, :code => :security_authentication, :desc => 'An error occurred while trying to authenticate.' }, { :status => 401, :code => :security_unauthorized, :desc => 'Valid credentials are required.' }, { :status => 400, :code => :search_param_invalid, :desc => 'A provided search query param is invalid.' }, { :status => 500, :code => :search_index_not_found, :desc => 'Index could not be queried for this application.' }, { :status => 500, :code => :internal_error, :desc => 'Internal Error.' }, { :status => 404, :code => :items_not_found, :desc => 'The requested items could not be found.' }, { :status => 412, :code => :item_version_mismatch, :desc => 'The version of the item does not match.' }, { :status => 412, :code => :item_already_present, :desc => 'The item is already present.' }, { :status => 400, :code => :item_ref_malformed, :desc => 'The provided Item Ref is malformed.' }, { :status => 409, :code => :indexing_conflict, :desc => 'The item has been stored but conflicts were detected ' + 'when indexing. Conflicting fields have not been indexed.' }, ] end


Like other DOT.NET Framework application RSS Bandit supports configuration options specified in a file named RssBandit.exe.config. The location is always the path you installed the program, usually C:\Program Files\RssBandit and can be edited with a normal text editor like Notepad or your favourite XML editor.
This file defines e.g. options to allow execution with currently released CLR runtime version (<startup><supportedRuntime> entries), but also some specific options only valid for RSS Bandit. This options are all located within one section named <appSettings>.
The recommended way to change some of the options there is to create a new file with the name "RssBandit.exe.user.config" in the same directory. You should remind we will overwrite the original RssBandit.exe.config file each time you install an update of the application! As long you choose the same folder for uninstall/reinstall your newly created file will be left untouched but used with your customized options.
The contents of the file must look like this:
<appSettings>
...
</appSettings>
Each application option entry must be specified in this format:
<add key="[option key]" value="[option value]" />
So here is a more complete sample:
<appSettings>
<add key="MaxDownloadThreads" value="4" />
</appSettings>
The following table describes all known and supported options with the allowed values.
Important note: the option keys must be spelled exactly as they are specified below!
|
Option key |
Option values |
Application Version |
|
MaxDownloadThreads |
Integer; default is 4; Recommended Range 1..16; Amount of threads used to request feeds. |
All |
|
UnconditionalCommentRss |
Boolean (true/false); default is false. By default we consider the feed element tag <slash:comments> (namespace http://purl.org/rss/1.0/modules/slash/) containing the amount of comments posted to detect if we have to request the comment feed for a particular post. If the counter is zero, we will not request the comment feed, but if you set this option to true, we will always request the comment feed regardless the value of the counter. |
v<IP_ADDRESS> and higher |
|
AutomaticColorSchemes |
Boolean (true/false); default is true. Set this to false if you consider any bad UI rendering effects while running software, that tweaks the windows default color scheme handling (Custom theme manager; Windows XP and higher) to provide "extended" schemes like the well known Window Blinds or StyleXP applications. |
v<IP_ADDRESS> and higher |
|
UseEnglishReplyPrefix |
Boolean (true/false); default is false. If you reply to a post (feed must support this), a short string is used as a prefix similar to Outlook/Outlook express. If you really like to have English reply title prefix "Re: " instead of the localized (in German e.g. "AW: "), set this value to true. |
v<IP_ADDRESS> and higher |
|
AppDataFolder |
String (Operating System Path); default is <Documents and Settings>\<LoggedOnUser>\Application Data\RssBandit; this is usually part of Windows Roaming User Profile (RUP). You can specify a custom path here like "D:\My Feeds\RSS Bandit". Do not forget to copy all the files from the original location and RSS Bandit should not be running while you do so! |
v<IP_ADDRESS> and higher |
|
AppCacheFolder |
String (Operating System Path); default is <Documents and Settings>\<LoggedOnUser>\Local Settings\ Application Data\RssBandit; this is usually not part of Windows Roaming User Profile (RUP). You can specify a custom path here like "D:\My Feeds\RSS Bandit". It is allowed to specify the same path as used for setting AppDataFolder to have all related files in one location. Do not forget to copy all the files from the original location and RSS Bandit should not be running while you do so! |
v<IP_ADDRESS> and higher |
|
Lucene.SearchIndexBehavior |
String; the following values are supported (case sensitive):
|
v<IP_ADDRESS> and higher |
|
UseCookiesFromIE |
Boolean (true/false); default is true. By default we send cookies written by Internet Explorer (if they match the site) along with the feed request. Some feeds/hosts manage access to the feeds by informations stored in a cookie. |
v<IP_ADDRESS> and higher |
|
RemoteFeedlist/ Ftp.ConnectionMode.Passive |
Boolean (true/false); default is true. Disable (false) this setting to configure the ACTIVE MODE for Synchronization using FTP. |
v<IP_ADDRESS> and higher |
|
Settings below are not anymore valid for v1.5.x: |
|
|
|
FeedDetailPane.ActiveXEnabled |
Boolean (true/false); default is false. Security setting for the feed detail view pane. Allow/Deny to execute Active X components. |
v<IP_ADDRESS>; v1.5.x uses the Web browser security settings instead |
|
FeedDetailPane.ImagesDownloadEnabled |
Boolean (true/false); default is true. Security setting for the feed detail view pane. Allow/Deny download/display if images. |
v<IP_ADDRESS>; v1.5.x uses the Web browser security settings instead |
|
FeedDetailPane.JavaEnabled |
Boolean (true/false); default is false. Security setting for the feed detail view pane. Allow/Deny to execute Java components. |
v<IP_ADDRESS>; v1.5.x uses the Web browser security settings instead |
|
FeedDetailPane.VideoEnabled |
Boolean (true/false); default is false. Security setting for the feed detail view pane. Allow/Deny to play video media components. |
v<IP_ADDRESS>; v1.5.x uses the Web browser security settings instead |
|
FeedDetailPane.FrameDownloadEnabled |
Boolean (true/false); default is false. Security setting for the feed detail view pane. Allow/Deny HTML frames support. |
v<IP_ADDRESS>; v1.5.x don't use it anymore |


Code Examples
Scroll down the page or select a chapter from the left to view the code examples.
Notes:
- The page numbers shown below aren't necessarily accurate if you have either the EPUB or Kindle version of the book, because pages may flow differently in those formats.
- "Fig" is short for "Figure" and "Figs" is short for "Figures."
- An item marked as an "Extra" represents an example that doesn't appear in the book.
- Chapters 2 and 21 aren't shown because they don't contain any examples.
Chapter 1 Web Page Building Blocks
- A Basic HTML Page
Page 4, Fig A: basic-html-page-no-content.html (no content)
Page 6, Fig C: basic-html-page.html (with content)
- A Web Page's Text Content
Page 12, Figs A and C: page-text-content.html
- A Browser's Default Display of Webpages
Page 24, Fig A: basic-html-page-with-2nd-paragraph.html
Chapter 3 Basic HTML Structure
Note: The majority of this chapter covers evolving the structure of a page's content. The individual steps are shown after this box. The end result is:
- Starting Your Web Page
Page 44, Fig A: starting-web-page.html
- Creating a Title
Page 48, Fig A: title-element.html
- Creating Headings
Page 50, Fig A: headings.html
Page 50, Fig B: headings-user-guide.html
Page 51, Fig C: headings-h1-h6-default-display.html
Page 52, Fig D: headings-subheading.html
- Creating a Header
Page 54, Fig A: header.html
Page 55, Fig D: header-two-header-elements.html
- Marking Navigation
Page 56, Fig A: nav.html
Page 58, Fig C: nav-deeper-look.html
- Marking the Main Area of a Webpage
Page 59, Fig A: main-content.html
- Creating an Article
Page 60, Fig A: article.html
Page 62, Figs C + D: article-with-footer-and-comments.html
- Defining a Section
Page 63, Fig A: section-news-site.html
Page 63, Fig B: section-graduation-program.html
- Specifying an Aside
Page 65, Fig A: aside.html
Page 68, Ex 1: aside-nested.html
Page 69, Ex 2: aside-with-nav.html
- Creating a Footer
Page 70, Fig A: footer.html
Page 72, Fig C: footer-larger.html
Page 72, Fig D: footer-nested-in-article.html
- Creating Generic Containers
Page 73, Fig A: div-none-but-page-styled.html
Page 74, Fig B + C: div-with-more-styling.html
Page 75, Fig D: div-no-page-styling.html
- Naming Elements with a Class or ID
Page 82, Fig A: naming-with-class-id.html
- Adding the Title Attribute to Elements
Page 84, Fig A: title-attribute.html
- Adding Comments
Page 85, Fig A: comments.html
Chapter 4 Text
- Starting a New Paragraph
Page 88, Fig A: paragraphs.html
- Specifying Fine Print
Page 89, Fig A: specifying-fine-print.html
- Marking Important and Emphasized Text
Page 90, Fig A: important-and-emphasized-text.html
- Creating a Figure
Page 92, Fig A: creating-a-figure.html
Page 93, Fig C: creating-a-figure-styled.html
- Indicating a Citation or Reference
Page 94, Fig A: indicating-a-citation.html
- Quoting Text
Page 95, Fig A: quoting-text-blockquote.html
Page 95, Fig B: quoting-text-blockquote-in-figure.html
Page 95, Fig D: quoting-text-q.html
Page 97, tip: quoting-text-q-nested.html
- Specifying Time
Page 98, Fig A: specifying-time.html
Page 99, Fig C: specifying-time-as-article-date.html
- Explaining Abbreviations
Page 101, Fig A: abbreviations.html
- Defining a Term
Page 103, Fig A: defining-instance-of-term.html
- Creating Superscripts and Subscripts
Page 104, Fig A: superscripts.html
- Adding Author Contact Information
Page 106, Fig A: author-contact-information.html
- Noting Edits and Inaccurate Text
Page 108, Fig A: noting-edits-del-ins.html
Page 109, Fig C: noting-edits-del-ins-example2.html
Page 110, Fig E: noting-inaccurate-text-s.html
- Marking Up Code
Page 112, Fig A: marking-up-code.html
Page 113 (sidebar): kbd-samp-var.html
- Using Preformatted Text
Page 114, Fig A: preformatted-text.html
- Highlighting Text
Page 116, Fig A: highlighting-text.html
Page 117, Fig C: highlighting-text-example2.html
- Creating a Line Break
Page 118, Fig A: creating-line-break.html
- Creating Spans
Page 120, Fig A: creating-spans.html
- Other Elements
Page 122 (
u
element): unarticulated-text.htmlPage 123 (
wbr
element): creating-line-break-opportunity.htmlPage 124 (
ruby
,rp
, andrt
elements): ruby-annotations.htmlPage 124 (
ruby
,rp
, andrt
elements): ruby-annotations-example2.htmlPage 127 (
bdi
element): bdi.htmlPage 128 (
meter
element): using-meter-to-provide-gauge.htmlPage 130 (
progress
element): indicating-progress.htmlPage 130 (
progress
element): indicating-progress-example2.html
Chapter 5 Images
Note: To keep the examples simple for this chapter, all the HTML and image files are in the same folder. This is absolutely not what I recommend you do when building your pages. Instead, it is common practice to keep your files organized by placing images in a separate folder ("img" and "images" are popular folder names). The path to your image in the src
value of each img
element needs to reflect this.
For example, currently the HTML that loads the image in specifying-img-size.html
is as follows (the image path is highlighted):

If you were to move cornermarket.jpg
to a folder called "images" in the same directory as the HTML page, you would need to change the src
value in the code as shown below:

You can see working examples in the Chapter 6 files (such as the first page, link-to-page.html
). They reference images in a folder called img
.
- Images for the Web: Size (dimensions)
Page 136, Fig C: size-big.html
Page 137, Fig D: size-small.html
- Images for the Web: Transparency
Page 138, Fig E1: transparency-stars-blue-bg.html
Page 138, Fig E2: transparency-stars-red-gradient-bg.html
- Inserting Images on a Page
Page 145, Fig A: inserting-an-image.html
- Offering Alternative Text
Page 147, Fig A: alternative-text.html
- Specifying Image Size
Page 151, Fig E: specifying-img-size.html
- Scaling Images with the Browser
Page 152, Fig A: scaling-image-before.html (image shows at native size of 440x340)
Page 152, Fig B: scaling-image-after.html (image is scaled down to 220x170)
- Adding Icons for Your Web Site
Page 155: favicon.html (referencing them in your page explicitly)
Chapter 6 Links
- Creating a Link to Another Web Page (and Other Link Basics)
Page 159, Fig B: link-to-page.html (it has a link to pioneer-valley.html)
Page 160, Fig E: link-to-page-on-another-site.html
Page 161, Fig H: block-level-link.html (it has a link to giraffe-escapes.html)
- Creating and Linking to Anchors
Page 164, Fig A: anchor-links.html
- Creating Other Kinds of Links
Page 166, Fig A: other-kinds-of-links.html
Chapter 7 CSS Building Blocks
- Constructing a Style Rule
Page 171, Figs B and C: constructing-a-rule.html
- Adding Comments to Style Rules
Pages 172-173, Figs A through E: css-comments.css
- Understanding Inheritance
Page 174, Fig A inheritance-not-styled.html (before CSS applied)
Page 175, Fig C inheritance.css (view results in inheritance.html)
- The Cascade: When Rules Collide
Page 177, Figs A and B: specificity.css and specificity.html
- A Property's Value
Page 184-185, Figs I and J: rgba.css and rgba.html
Chapter 8 Working with Style Sheets
- Creating an External Style Sheet
Page 190, Fig A: style.css
- Linking to External Style Sheets
Page 192, Figs A and B: style.css and link-external-style-sheet.html
Page 193, Fig D: link-external-style-sheet-example2.html (it also uses style.css)
- Creating an Embedded Style Sheet
Page 194, Fig A: create-embedded-style-sheet.html
- Applying Inline Styles
Page 196, Fig A: apply-inline-style.html
Page 197, Fig C and D: class-instead-of-inline.html and class-instead-of-inline.css
- The Cascade and the Order of Styles
Page 198, Fig A: style.css
Page 198, Fig B: importance-of-location.html (it also uses style.css)
Page 199, Fig D: importance-of-location-example2.html (it also uses style.css)
- Using Media-Specific Style Sheets
Page 200, Fig A: media-specific-style-sheets.html (it also uses style.css and print.css)
Page 201, Fig B: base-styles-plus-media-print.css (view results in base-styles-plus-media-print.html)
Chapter 9 Defining Selectors
- Selecting Elements by Name
Pages 206-207, Figs A and B: select-by-element-name.html and name.css
- Selecting Elements by Class or ID
Pages 208-209, Figs A and B: select-by-class.html and class.css
Page 210 (third tip): select-by-class-two-classes.html and class-two-classes.css
Page 210 (second-to-last tip): select-by-id.html and id.css
- Selecting Elements by Context
Page 212, Figs A-C: select-by-context.html and context.css (includes Figs B and C)
Page 214, Fig E: child-selector.css (view results in select-by-child-selector.html)
Page 215, Fig G: adjacent-sibling-combinator.css (view results in select-by-adjacent-sibling.html)
- Selecting an Element That Is the First or Last Child
Page 216, Figs A and B: select-by-first-child.html and first-child-selector.css
Page 217, Fig D: last-child.css (view results in select-by-last-child.html)
- Selecting the First Letter or First Line of an Element
Page 218, Figs A and B: select-by-first-letter.html and first-letter.css
Page 219, Fig D: first-line.css (view results in select-by-first-line.html)
- Selecting Links Based on Their State
Page 220, Figs A and B: select-links-by-state.html and links-by-state.css
- Selecting Elements Based on Attributes
Page 222, Figs A and B: select-by-attribute.html and attribute-selector.css
- Specifying Groups of Elements
Pages 226, Figs A and B: select-by-groups.html and groups.css
- Combining Selectors
Page 227, Fig A: combining-selectors.css (view results in combining-selectors.html)
Chapter 10 Formatting Text with Styles
Note: The majority of this chapter covers evolving the formatting of a page's text and background. The individual steps are shown after this box. The end result is:
text-final.html (it uses text-decoration.css)
-
Page 231, Fig C: no-styles.html (page without any CSS applied except browser defaults)
- Choosing a Font Family
Page 232, Fig A: font-family.css (view results in font-family.html)
- Specifying Alternate Fonts
Page 233, Fig A: font-family-alternate.css (view results in font-family-alternate.html)
- Creating Italics
Page 236, Fig A: font-style-italic.css (view results in font-style-italic.html)
Page 237 (sidebar) real-italic-vs-faux-italic.html
- Applying Bold Formatting
Page 238, Fig A: font-weight-bold.css (view results in font-weight-bold.html)
Page 239, Fig D: font-weight-bold-numeric-weights.html
Page 239, Fig E: real-bold-vs-faux-bold.html
- Setting the Font Size
Page 240, Fig A: font-size-pixels.css (view results in font-size-pixels.html)
Page 241, Fig D: font-size-ems.css (view results in font-size-ems.html)
Page 243, Fig G: font-size-rems.css (view results in font-size-rems.html)
- Setting the Line Height
Page 245, Fig A: line-height.css (view results in line-height.html)
- Setting All Font Values at Once
Page 246, Fig A: font-shorthand.css (view results in font-shorthand.html)
- Setting the Color
Page 248, Fig A: color.css (view results in color.html)
- Changing the Text's Background
Page 250, Fig A: background-color-figa.css (view results in background-color-figa.html)
Page 251, Fig C: background-color-figc.css (view results in background-color-figc.html)
Page 251, Fig E: background-color-plus-padding.css (view results in background-color-plus-padding.html)
Page 252, Fig G: background-img-repeat.css (view results in background-img-repeat.html)
Page 253, Fig I: background-img-repeat-x-white-bg.html
Page 253, Fig K (shown on left side of L): background-img-repeat-x-black-bg.html
This doesn't have the rule in Fig K fthat setshtml { min-height: 100%; }
Page 253, Fig K (shown on right side of L): background-img-repeat-x-black-bg-2.html
Page 254, Fig M: background-img-fixed.html
Page 254, Fig O: background-img-scroll.html (the default behavior)
Page 255, Fig P: background-img-position.css (view results in background-img-position.html)
- Controlling Spacing
Page 257, Fig A: letter-spacing.css (view results in letter-spacing.html)
- Adding Indents
Page 258, Fig A: text-indent.css (view results in text-indent.html)
- Aligning Text
Page 259, Fig A: aligning-text.css (view results in aligning-text.html)
- Changing the Text Case
Page 260, Fig A: text-case.css (view results in text-case.html)
- Using Small Caps
Page 261, Fig A: small-caps.css (view results in small-caps.html)
- Decorating Text
Page 262, Fig A: text-decoration.css (view results in text-decoration.html)
- Setting White Space Properties
Page 264, Fig A: whitespace.css and whitespace.html
Chapter 11 Layout with Styles
Note: The vast majority of this chapter entails evolving a single page layout for the fictitious Le Journal site. The CSS file progresses throughout the chapter, building upon the styles added in the previous pages. The end result is:
- Structuring Your Pages
Pages 269-270, Fig A: no-styles.html
(the same as finished-page.html, but without any CSS applied except browser defaults) - Styling HTML5 Elements in Older Browsers
Pages 272-273, Figs A and E: html5-elements-styling.css (step 1) and html5-elements-styling.html (step 2, last paragraph)
- Resetting or Normalizing Default Styles
Page 274, Fig A: reset.html and reset.css
Page 275, Fig B: normalize.html and normalize.css
- Setting the Border
Page 291, Fig K: border-styles.html
- Determining How to Treat Overflow
Page 305, Fig A: overflow.html
- Aligning Elements Vertically
Page 306, Figs A and C: vertical-align.html and vertical-align.css
Chapter 12 Building Responsive Webpages
- Putting It All Together
The finished page as shown in the steps throughout section:
Page 326-331, Figs A-K: finished-page.html and lejournal.css
Chapter 13 Working with Web Fonts
Note: The filenames in the samples below do not always correlate to the filenames mentioned in the chapter. For instance, both example.css
and styling-text-with-web-font.html
evolve during the chapter, so I had to create multiple versions of them to provide you the files in their various stages.
- Understanding the @font-face Rule
Page 345, Fig A: understanding-font-face.css
- Styling Text with a Web Font
Page 346-347, Figs A and E: example-regular.html and example-regular.css
- Applying Italics and Bold with a Web Font
Page 351, Fig F: example-regular-italic.css (you can view it in example-regular-italic.html)
Page 353, Fig K: example-regular-italic-bold.css (you can view it in example-regular-italic-bold.html)
Page 354-355, Figs M and P: example-regular-italic-bold-and-bolditalic.css (you can view it in example-regular-italic-bold-and-bolditalic.html)
Page 356, Last tip and Fig R: styling-text-with-web-font.html and example.css
- Using Web Fonts from Google Fonts
Page 358-359, Figs F and G: using-google-web-font.html and style.css
Chapter 14 Enhancements with CSS3
- Rounding the Corners of Elements
Page 365, Figs A and B: rounded-corners.html and rounded-corners.css
Page 367, Fig F: rounded-corners-more-examples.css (view results in rounded-corners-more-examples.html)
- Adding Drop Shadows to Text
Page 368, Figs A and B: text-shadow.html and text-shadow.css
- Adding Drop Shadows to Other Elements
Page 370, Figs A, B and D: box-shadow.html and box-shadow.css
- Applying Multiple Backgrounds
Page 373, Figs A and B: multiple-backgrounds.html and multiple-backgrounds.css
Page 375, Fig E: multiple-backgrounds-shorthand.css (view results in multiple-backgrounds-shorthand.html)
- Using Gradient Backgrounds
Page 376-379, Figs A-I: gradients.html and gradients.css
- Setting the Opacity of Elements
Page 382, Fig A: opacity-default.html (it also uses opacity-default.css)
Page 382, Fig C: opacity-example-2.css (view results in opacity-example-2.html)
Page 383, Fig E: opacity-example-3.html and opacity-example-3.css
Page 395, Fig E: opacity-plus-ie-filter.css (view results in opacity-plus-ie-filter.html)
- Effects with Generated Content
Page 384, Fig A: generated-content-without.html
Page 384, Fig C: generated-content-arrows.html
Page 384-385, Figs E and F: generated-content-bubble-triangle.html and generated-content-bubble-triangle.css
- Combining Images with Sprites
Page 387, Fig A: sprite-without.html (it uses sprite-without.css)
Pages 388, Fig C: sprite.css (it uses the same HTML as in Fig A: sprite.html)
Chapter 15 Lists
- Creating Ordered and Unordered Lists
Page 390, Fig A: ordered-list.html
Page 391, Fig C: unordered-list.html
Page 391-392, Figs E and F: unordered-list-spacing.html and unordered-list-spacing.css
- Choosing Your Markers
Page 393, Figs A and B: choosing-your-markers.html (the CSS is embedded in the HTML)
- Using Custom Markers
Page 394, Figs A and B: using-custom-markers-step1.html and using-custom-markers-step1.css
Page 395, Fig D: using-custom-markers-step2.html and using-custom-markers-step2.css
Page 396, Fig F: using-custom-markers-step3.html and using-custom-markers-step3.css
- Choosing Where to Start List Numbering
Page 397, Fig A: choosing-where-to-start-numbering.html
- Controlling Where Markers Hang
Page 398, Fig A: controlling-where-markers-hang-default.html
Page 398, Fig B: controlling-where-markers-hang-inside.html (the CSS is embedded in the HTML)
- Setting All List-Style Properties at Once
Page 399, Fig A: setting-all-list-style-properties.html (the CSS is embedded in the HTML)
- Styling Nested Lists
Page 400-401, Figs A and B: nested-lists.html and nested-lists.css
Extra Example—how to style with classes instead (per second tip on page 401):
nested-lists-with-classes.html and nested-lists-with-classes.css - Creating Description Lists
Page 404, Figs A and B: description-list.html (the CSS is embedded in the HTML)
Page 405, Figs D and E: description-list-multiple-dts.html (the CSS is embedded in the HTML)
Page 406-407, Figs G and H: description-list-nested.html and description-list-nested.css
Chapter 16 Forms
- Creating Forms
Page 413, Fig A: form.html (This is the main form discussed throughout the chapter.)
Page 414, Fig B: form.css
- Processing Forms
Extra (referenced on Page 416, Fig A): show-data.php
NOTE: This sample PHP script collects the information submitted with
form.html
and displays it in your browser. You will need to put the file on a web server for it to work. Open this file in your text editor and read the comment that begins with "TO TEST THIS SCRIPT" for additional important instructions.Extra (referenced on Page 416, Fig A): email-data.php
NOTE: This sample PHP script collects the information submitted with the form and emails it to the email addresses you specify in the script. You will need to put it on a web server for it to work. Open this file in your text editor and read the comment that begins with "TO TEST THIS SCRIPT" for additional important instructions.
- Disabling Form Elements
Page 444, Fig A: disabled.html
Page 445, first tip: toggle-textarea.js (
disabled.html
uses it)
Chapter 17 Video, Audio, and Other Multimedia
Reminder: Include the HTML5 shiv in the head
of your HTML if you'd like Internet Explorer 6, 7, and 8 to recognize HTML5 elements and render the CSS you apply to them. (It *won't* make the video
or audio
element work in these older browsers, though, because they don't support those HTML5 elements.) See "The HTML5 Shiv" in Chapter 11 (p.273) for details, or see http://code.google.com/p/html5shiv/.
- Adding a Single Video to Your Webpage
Page 452, Fig A: webm-video.html
Page 452, Fig C: webm-video-with-dimensions.html
- Adding Controls and Autoplay to Your Video
Page 454, Fig A: webm-video-with-controls.html
Page 455, Fig G: webm-video-with-controls-autoplay.html
- Looping a Video and Specifying a Poster Image
Page 456, Fig A: webm-video-with-autoplay-loop.html
Extra: mp4-video-with-autoplay-loop.html
Page 456, Fig B: webm-video-with-poster-image.html
- Preventing a Video from Preloading
Page 457, Fig A: webm-video-with-preload-none.html
Page 458, sidebar: webm-video-with-preload-metadata.html
- Using Video with Multiple Sources and a Text Fallback
Page 459, Fig A: video-with-sources-and-text-link-fallback.html
- Adding an Audio File with Controls to Your Webpage
Page 464, Fig A: ogg-audio-with-controls.html
- Autoplaying, Looping, and Preloading Audio
Page 466, Fig A: ogg-audio-with-autoplay-controls.html
Page 466, Fig C: ogg-audio-with-loop-controls.html
Page 466, Fig D: ogg-audio-with-preload-metadata.html
- Providing Multiple Audio Sources with a Fallback
Page 468, Fig A: audio-with-sources-and-link-fallback.html
- Adding Video and Audio with a Flash Fallback
Page 471, Fig D: video-audio-flash-fallback-add-file-to-head.html
Page 472, Fig F: video-flash-fallback.html
Page 473, Fig H: audio-flash-fallback.html
Page 473, Fig J: video-and-audio-flash-fallback.html
Chapter 18 Tables
- Structuring Tables
Page 478, Fig A: table.html
Page 479, Fig C: table-evolved.html
Page 479-480, Figs C and E: table-evolved-and-styled.css and table-evolved-and-styled.html
(the same as table-evolved.html except it loads the style sheet) - Spanning Columns and Rows
Page 482, Fig A: table-with-colspan-rowspan.html and table-with-colspan-rowspan.css (Extra)
Chapter 19 Working with Scripts
Note: You won't gain much from viewing these pages in a browser; their value is in the HTML code and comments. In particular, I've included comments in load-before-body-end-tag.html and load-before-body-end-tag-subfolder.html about why the code in the latter is preferred from the standpoint of organizing your JavaScript files.
- Loading an External Script
Page 487, Fig A: load-before-body-end-tag.html
Extra: load-before-body-end-tag-subfolder.html (preferred; it loads the JS from a sub-folder)
Page 487, Fig B: load-in-head.html
Page 488, Fig C: js/sample-script.js
Page 489, Fig D: js/sample-script.min.js
- Adding an Embedded Script
Page 492, Fig A: embedded-before-body-end-tag.html
Page 492, Fig B: embedded-in-head.html
Chapter 20 Testing & Debugging Web Pages
- Validating Your Code
W3C HTML Validator (including HTML5)
Page 496-497, Fig A and C: h1-error.html
Extra (as shown in Fig E): h1-fixed.html
- Testing Your Pages
Page 498, Fig B: testing-your-page-error.html
- When Images Don't Display
Page 510, Fig A: image-does-not-display.html
Extra: image-displays.html


This is the island's main settlement. It lies on its western side. The island is almost completely surrounded by a reef.
Position 00° 31'43"S, 166° 54'31"E (harbour entrance)
This small harbour lies just north of the phosphate exporting infastructure.
It is unknown whether it is possible for yachts to enter and/or moor here.
Last updated April 2016.


This is a string option whose default value is "", meaning that the generated Token class will extend java.lang.Object. This option may be set to the name of a class that will be used as the base class for the generated Token class.
""
java.lang.Object


Home
About Us
Services
Contact Us
We help you to keep your place clean
We offer a wide range of cleaning services for catering to both residential and corporate


thingsIO
Dashboard
Schema
Analytics
Sign out
Analytics
Export
Statistics
Forecasting
Filename
File format
Choose...
JSON
CSV
timestamp (UTC)
field_1
field_2
field_3
Download
...
...


Hazelcast allows you to load and store the distributed map entries from/to a persistent data store such as a relational database. For these, you can use Hazelcast's MapStore
and MapLoader
interfaces.
When you provide a MapLoader
implementation and request an entry (IMap.get()
) that does not exist in the memory, MapLoader
's load
or loadAll
methods will load that entry from the data store. This loaded entry is placed into the map and will stay there until it is removed or evicted.
When a MapStore
implementation is provided, an entry is put also into a user defined data store.
NOTE: Data store needs to be a centralized system that is accessible from all Hazelcast Nodes. Persisting to local file system is not supported.
Please see the below example.
public class PersonMapStore implements MapStore<Long, Person> {
private final Connection con;
public PersonMapStore() {
try {
con = DriverManager.getConnection("jdbc:hsqldb:mydatabase", "SA", "");
con.createStatement().executeUpdate(
"create table if not exists person (id bigint, name varchar(45))");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public synchronized void delete(Long key) {
System.out.println("Delete:" + key);
try {
con.createStatement().executeUpdate(
format("delete from person where id = %s", key));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public synchronized void store(Long key, Person value) {
try {
con.createStatement().executeUpdate(
format("insert into person values(%s,'%s')", key, value.name));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public synchronized void storeAll(Map<Long, Person> map) {
for (Map.Entry<Long, Person> entry : map.entrySet())
store(entry.getKey(), entry.getValue());
}
public synchronized void deleteAll(Collection<Long> keys) {
for (Long key : keys) delete(key);
}
public synchronized Person load(Long key) {
try {
ResultSet resultSet = con.createStatement().executeQuery(
format("select name from person where id =%s", key));
try {
if (!resultSet.next()) return null;
String name = resultSet.getString(1);
return new Person(name);
} finally {
resultSet.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public synchronized Map<Long, Person> loadAll(Collection<Long> keys) {
Map<Long, Person> result = new HashMap<Long, Person>();
for (Long key : keys) result.put(key, load(key));
return result;
}
public Set<Long> loadAllKeys() {
return null;
}
}
NOTE: Loading process is performed on a thread different than the partition threads using ExecutorService.
RELATED INFORMATION
For more MapStore/MapLoader code samples please see here.
Hazelcast supports read-through, write-through and write-behind persistence modes which are explained in below subsections.
If an entry does not exist in the memory when an application asks, Hazelcast asks your loader implementation to load that entry from the data store. If the entry exists there, the loader implementation gets it, hands it to Hazelcast, and Hazelcast puts it into the memory. This is read-through persistence mode.
MapStore
can be configured as write-through by setting the write-delay-seconds
property to 0. This means the entries will be put to the data store synchronously.
In this mode, when the map.put(key,value)
call returns, you can be sure that
MapStore.store(key,value)
is successfully called so the entry is persisted.
In-Memory entry is updated
In-Memory backup copies are successfully created on other JVMs (if backup-count
is greater than 0)
Same behavior goes for the map.remove(key)
, only difference is that MapStore.delete(key)
is called when it will be deleted.
If MapStore
throws an exception, then the exception will be propagated back to the original put
or remove
call in the form of RuntimeException
.
MapStore
can be configured as write-behind by setting the write-delay-seconds
property to a value bigger than 0. This means the modified entries will be put to the data store asynchronously after a configured delay.
NOTE: In write-behind mode, Hazelcast coalesces updates on a specific key, i.e. applies only the last update on it.
In this mode, when the map.put(key,value)
call returns, you can be sure that
In-Memory entry is updated
In-Memory backup copies are successfully created on other JVMs (if backup-count
is greater than 0)
The entry is marked as dirty so that after write-delay-seconds
, it can be persisted with MapStore.store(key,value)
call.
Same behavior goes for the map.remove(key)
, only difference is that MapStore.delete(key)
is called when it will be deleted.
If MapStore
throws an exception, then Hazelcast retries to store the entry. If it still cannot be stored, a log message is printed and the entry is re-queued.
For batch write operations, which are only allowed in write-behind mode, Hazelcast will call MapStore.storeAll(map)
, and MapStore.deleteAll(collection)
to do all writes in a single call.
NOTE: If a map entry is marked as dirty, i.e. it is waiting to be persisted to the MapStore
in a write-behind scenario, the eviction process forces the entry to be stored. By this way, you will have control on the number of entries waiting to be stored, so that a possible OutOfMemory exception can be prevented.
NOTE: MapStore or MapLoader implementations should not use Hazelcast Map/Queue/MultiMap/List/Set operations. Your implementation should only work with your data store. Otherwise, you may get into deadlock situations.
Here is a sample configuration:
<hazelcast>
...
<map name="default">
...
<map-store enabled="true">
<!--
Name of the class implementing MapLoader and/or MapStore.
The class should implement at least of these interfaces and
contain no-argument constructor. Note that the inner classes are not supported.
-->
<class-name>com.hazelcast.examples.DummyStore</class-name>
<!--
Number of seconds to delay to call the MapStore.store(key, value).
If the value is zero then it is write-through so MapStore.store(key, value)
will be called as soon as the entry is updated.
Otherwise it is write-behind so updates will be stored after write-delay-seconds
value by calling Hazelcast.storeAll(map). Default value is 0.
-->
<write-delay-seconds>60</write-delay-seconds>
<!--
Used to create batch chunks when writing map store.
In default mode all entries will be tried to persist in one go.
To create batch chunks, minimum meaningful value for write-batch-size
is 2. For values smaller than 2, it works as in default mode.
-->
<write-batch-size>1000</write-batch-size>
</map-store>
</map>
</hazelcast>
As you know, a configuration can be applied to more than one map using wildcards (Please see Using Wildcard), meaning the configuration is shared among the maps. But, MapStore
does not know which entries to be stored when there is one configuration applied to multiple maps. To overcome this, Hazelcast provides MapStoreFactory
interface.
Using this factory, MapStore
s for each map can be created, when a wildcard configuration is used. A sample code is given below.
Config config = new Config();
MapConfig mapConfig = config.getMapConfig( "*" );
MapStoreConfig mapStoreConfig = mapConfig.getMapStoreConfig();
mapStoreConfig.setFactoryImplementation( new MapStoreFactory<Object, Object>() {
@Override
public MapLoader<Object, Object> newMapStore( String mapName, Properties properties ) {
return null;
}
});
Moreover, if the configuration implements MapLoaderLifecycleSupport
interface, then the user will have the control to initialize the MapLoader
implementation with the given map name, configuration properties and the Hazelcast instance. See the below code portion.
public interface MapLoaderLifecycleSupport {
/**
* Initializes this MapLoader implementation. Hazelcast will call
* this method when the map is first used on the
* HazelcastInstance. Implementation can
* initialize required resources for the implementing
* mapLoader such as reading a config file and/or creating
* database connection.
*/
void init( HazelcastInstance hazelcastInstance, Properties properties, String mapName );
/**
* Hazelcast will call this method before shutting down.
* This method can be overridden to cleanup the resources
* held by this map loader implementation, such as closing the
* database connections etc.
*/
void destroy();
}
MapLoader.loadAllKeys
API is used for pre-populating the in-memory map when the map is first touched/used. If MapLoader.loadAllKeys
returns NULL then nothing will be loaded. Your MapLoader.loadAllKeys
implementation can return all or some of the keys. You may select and return only the hot
keys, for instance. Also note that this is the fastest way of pre-populating the map as Hazelcast will optimize the loading process by having each node loading owned portion of the entries.
Moreover, there is InitialLoadMode configuration parameter in the class MapStoreConfig
class. This parameter has two values: LAZY and EAGER. If InitialLoadMode is set as LAZY, data is not loaded during the map creation. If it is set as EAGER, whole data is loaded while the map is being created and everything becomes ready to use. Also, if you add indices to your map by MapIndexConfig
class or addIndex
method, then InitialLoadMode is overridden and MapStoreConfig behaves as if EAGER mode is on.
Here is MapLoader initialization flow:
getMap()
is first called from any node, initialization will start depending on the the value of InitialLoadMode. If it is set as EAGER, initialization starts. If it is set as LAZY, initialization actually does not start but data is loaded at each time a partition loading is completed.MapLoader.loadAllKeys()
to get all your keys on each nodeMapLoader.loadAll(keys)
IMap.putTransient(key,value)
NOTE: If the load mode is LAZY and when clear()
method is called (which triggers MapStore.deleteAll()
), Hazelcast will remove ONLY the loaded entries from your map and datastore. Since the whole data is not loaded for this case (LAZY mode), please note that there may be still entries in your datastore.
The method loadAll
is developed to load some or all keys into a data store in order to optimize the multiple load operations. The method has two signatures (i.e. same method can take two different parameter lists). One loads the given keys and the other loads all keys. Please see the sample code below.
public class LoadAll {
public static void main(String[] args) {
final int numberOfEntriesToAdd = 1000;
final String mapName = LoadAll.class.getCanonicalName();
final Config config = createNewConfig(mapName);
final HazelcastInstance node = Hazelcast.newHazelcastInstance(config);
final IMap<Integer, Integer> map = node.getMap(mapName);
populateMap(map, numberOfEntriesToAdd);
System.out.printf("# Map store has %d elements\n", numberOfEntriesToAdd);
map.evictAll();
System.out.printf("# After evictAll map size\t: %d\n", map.size());
map.loadAll(true);
System.out.printf("# After loadAll map size\t: %d\n", map.size());
}
}
In some scenarios, you may need to modify the object after storing it into the map store.
For example, you can get ID or version auto generated by your database and you need to modify your object stored in distributed map, not to break the sync between database and data grid. You can do that by implementing PostProcessingMapStore
interface;
so the modified object will be put to the distributed map. That will cause an extra step of Serialization
, so use it just when needed (This explanation is only valid when using write-through
map store configuration).
Here is an example of post processing map store:
class ProcessingStore extends MapStore<Integer, Employee> implements PostProcessingMapStore {
@Override
public void store( Integer key, Employee employee ) {
EmployeeId id = saveEmployee();
employee.setId( id.getId() );
}
}