Also available as: PDF.
Abstract
Abstract. Parsing Expression Grammars (PEGs) are specifications of unambiguous recursive-descent style parsers. PEGs incorporate both lexing and parsing phases and have valuable properties, such as being closed under composition. In common with most recursive-descent systems, raw PEGs cannot handle left-recursion; traditional approaches to left-recursion elimination lead to incorrect parses. In this paper, I show how the approach proposed for direct left-recursive Packrat parsing by Warth et al. can be adapted for ‘pure’ PEGs. I then demonstrate that this approach results in incorrect parses for some PEGs, before outlining a restrictive subset of left-recursive PEGs which can safely work with this algorithm. Finally I suggest an alteration to Warth et al.’s algorithm that can correctly parse a less restrictive subset of directly recursive PEGs.
1 Introduction
Parsing is the act of discovering the structure of text with respect to a particular grammar. Parsing algorithms such as LL and LR parsing were developed alongside the first generation of high level programming languages. In general, such parsing algorithms aim to parse subsets of the full class of Context Free Grammars (CFGs). By limiting the class of parseable languages, such algorithms are both time and space efficient, considerations that were of huge practical importance given the performance limitations of hardware available at the time. In this classical sense, parsing is generally split into two phases. In the first phase, tokenization (or ‘lexing’) splits the input into distinct tokens (or ‘lexemes’), which can be broadly thought of as being similar to splitting English text into words. In the second phase, the token stream is parsed, checking that it conforms to the user’s grammar and, optionally, ordering it into a tree structure (the parse tree). While both tokenizers and grammars can be written by hand, it is common to use specialised tools to generate them from customised domain specific languages (e.g. the ubiquitous yacc tool). The speed of modern computers means that relatively inefficient approaches to parsing are now often practical. For example, Earley’s algorithm [1] can parse the entire class of CFGs; while it is O(n3), even a simple implementation can parse in the low thousands of lines per second [8]. For many people, parsing is a solved problem: there are a wide variety of well understood algorithms, with a reasonable body of knowledge about which are the most suitable in differing circumstances.
Traditional parsing algorithms have one major weakness: it is at best difficult, and generally impossible, to combine two parsers. For example, one may wish to embed SQL inside Java so that SQL expressions can be used wherever a Java expression is expected; such examples require a parser which understands both constituent languages. Ideally one would like to take existing, separate, Java and SQL parsers and combine them. Many parsing algorithms restrict the class of grammars they can accept (in general to a subset of CFGs); for most such approaches, combining two valid grammars can lead to a grammar which falls outside the class of valid grammars. Equally critically, the separation of the tokenization and parsing phases hinders switching between the different language parsers. Put simply, since tokenizers are largely stateless they cannot know when to stop tokenizing (meaning that the Java tokenizer may raise an error when it comes across SQL input)—attempts to hack around this problem are rarely pleasant and generally fragile.
Visser introduced arguably the first practical scannerless parsing approach [9], which unites the tokenization and parsing phases, making the combination of grammars practical. Visser’s approach has no theoretical problem combining grammars, since it can parse the full class of CFGs, and CFGs are closed under composition—however a combined grammar may contain new ambiguities relative to its constituent parts. Since the static detection of ambiguity is, in general, impossible, this can make grammar composition a difficult exercise in practice.
Ford introduced Parsing Expression Grammars (PEGs) [3] which are an alternative approach to scannerless parsing. PEGs have three major differences when compared to other parsing approaches. First, the class of languages PEGs can express has no relation to CFGs. Second, PEGs describe an unambiguous parser; a string is either not parsed by a PEG or has a single unambiguous parse. Third, PEG parsers are closed under union, intersection, and complement. Because of this last point, PEGs are of substantial interest to those looking to combine parsers (see e.g. [4, 7]).
Although PEGs can perform limited context sensitive parsing (through semantic predicates, which are effectively side-conditions on parsing rules), in practice they resemble a combination of simple regular expressions and recursive-descent parsing. Because of this latter point, PEGs cannot handle left-recursion, which is when a rule refers to itself before any input has been consumed. For example – assuming a suitable tokenizer – a typical CFG grammar for a subset of mathematical expressions might be expressed as follows:
The directly translated PEG parser looks as follows:
Unfortunately, as with recursive descent parsers in general, the left-recursion in the Expr rule means that a PEG parser will enter an infinite loop and never terminate. Since many standard grammar constructs are most naturally represented with left-recursion, this limitation is often frustrating in practice. As I show in Section 3, traditional left-recursion techniques cannot provide the expected result with PEGs. Thus, while grammars can be manually rewritten to avoid left-recursion, it is difficult to verify that the language accepted by the rewritten grammar is identical to the original.
Warth et al. recently proposed a modification to handle left-recursion in Packrat parsers [10]. Packrat parsers are essentially an optimisation of PEGs, utilising memoisation to make Packrat parsers O(n) [2]; Warth et al.’s approach relies on Packrat memoisation. Warth et al. first present a relatively simple adaption of Packrat parsing to cope with direct left-recursion, before detailing a much more complicated adaption to cope with indirect left-recursion. As will become apparent as this paper continues, direct recursion raises sufficiently many issues that I concentrate solely on it. It should be noted that while Packrat parsing obviously adds an extra layer of complexity over ‘pure’ PEGs, contrary to expectations it often slows parsing down when used blindly for every rule [6].
The first research challenge tackled in this paper is: can Warth et al.’s approach be adapted to a ‘pure’ PEG parser without Packrat memoisation (Section 4.2)? Having then identified that certain PEG grammars then lead to incorrect parses (Section 5) the second research question tackled is: can a well-defined subset of PEGs be parsed correctly with Warth et al.’s algorithm (Section 6.1)? The final research question tackled is: can Warth et al.’s algorithm be modified so that a wider set of directly recursive PEGs be parsed correctly (Section 6.3)?
2 PEG overview
A full overview of PEGs can be found in Ford’s original paper [3]. This section is a brief refresher.
| "s" | Match string literal s |
| [c] | Match against character class c |
| . | Match any character |
| (e) | Grouping |
| e? | Match e zero or one times |
| e* | Match e zero or more times |
| e+ | Match e one or more times |
| &e | Non-consuming match of e |
| !e | Non-consuming negative match of e |
| e1 e2 | Sequence |
| e1 / e2 | Ordered choice |
Figure 1 shows the PEG operators. The crucial difference between PEGs and CFGs is the ordered choice operator e1 / e2. This means ‘try e1 first; if it succeeds, the ordered choice immediately succeeds and completes. Only if e1 fails should e2 be tried.’ In other words, once an earlier part of an ordered choice matches against a piece of text, the ordered choice has committed to that piece of text, and latter parts of the ordered choice are never tried. Therefore an ordered choice a / ab will not match against the complete string ‘ab’ as the ordered choice immediately matches a, succeeds, and does not try the second part of the ordered choice. This in contrast to CFGs where both e1 and e2 are on an equal footing.
The &e and !e operators are semantic predicates which succeed if e matches or does not match, respectively; note that they do not advance the input position. Semantic predicates are the other significant difference between PEGs and CFGs.
Most other aspects of PEGs are similar to CFGs. A PEG grammar consists of one or more rules; rules are referenced by their name. In keeping with Ford’s original definition, a PEG rule R with a body expression e is written R <- e.
3 Traditional left-recursion removal
Put simply, left-recursive PEGs are those where a rule calls itself without advancing the input position being parsed; in other words, left-recursive PEGs cause infinite loops. Consider this example from Warth et al.:
When this parses against an input, the first action of the expr rule is to call itself; once called, the expr rule then calls itself again recursively. Since no input is consumed before expr calls itself, no progress is ever made (indeed, the general result of running such a PEG is a ‘stack exhausted’ error). As expr calls itself directly, this example shows a direct left-recursive rule.  Indirect left-recursion adds a number of challenges over direct left-recursion which I do not consider further in this paper.
The problem of left-recursion is a long-standing issue in LL and recursive-descent
parsing. In this section I show a simplified version of the standard technique for
left-recursion elimination from parsing theory (see e.g. [5] for more details), and
demonstrate why it is not suitable for PEGs. In essence, a left-recursive
rule:
R → R a
| b
can be translated to the right-recursive:
R → b R′
R′→ a R′
| ε
The language accepted by the latter non-left-recursive CFG is provably the same
as the former left-recursive CFG. It therefore seems sensible to apply this
approach to left-recursive PEG, rewriting the example from above as
follows:
If we use this new PEG to parse the input 1-2-3 and write a standard tree walker over it to evaluate the expression we get the answer 2, instead of the expected -4. Where we expect 1-2-3 to parse as equivalent to (((1)-2)-3), it has in fact been parsed as the equivalent of (1-(2-(3))); the left-recursion elimination has turned the left-associative left-recursive PEG into a right-associative right-recursive PEG.
Attempting to restore left-associativity is difficult and, in the presence of semantic actions or predicates, impossible. Grimm’s Rats! [4] uses traditional left-recursion elimination but ‘promises’ to transparently transform the resulting right-associative parse tree into the equivalent left-associative structure. This technique is only applicable to those directly left-recursive rules which do not call external code (e.g. no semantic predicates), as the order that the text is parsed is still incorrect; thus, actions cannot appear in such rules, as they could unwittingly be victims of the incorrect parse order.
In summary, na´ve traditional left-recursion destroys left-associativity. While in certain limited situations left-associativity can be restored ex post facto, this is not a general solution.
4 Warth et al.’s proposal
Warth et al. propose a solution to both direct and indirect left-recursion in PEGs. Their approach is based on the concept of ‘growing the seed’. In essence, when direct left-recursion on a rule R at input position P is detected a default seed value (initially set to a ‘fail’ value) is immediately returned for subsequent references to R, meaning that infinite recursion is avoided. An unbounded loop is then started; whenever the body of R matches input successfully, the seed is updated (or ‘grown’), and R is reevaluated at input position P. When no further input is matched, or if less input than the previous iteration is matched, the loop is finished. The seed value (which may not have been grown beyond ‘fail’) is then returned. In essence, the parser turns from (recursive) top-down in normal operation to (iterative) bottom-up when left-recursion is detected.
In this section I first show that Warth et al.’s approach is not limited to Packrat parsers and can be naturally applied to pure PEGs (Section 4.2). I also provide a much more detailed explanation of the parser than Warth et. al.
4.1 Conventions
Sets are mutable datatypes (used conventionally otherwise), written as {E0,...,En}. An element E can be added to a set S with ADD(S, E) and removed with DEL(S, E).
Maps are data types relating keys to elements. A map is written <K0:E0, ..., Kn:En> where K represents a key and E an element. In this paper, keys are either integers or rule names; elements can be of an arbitrary type. The existence of a key K within a map M can be checked with K ∈ M. The corresponding element E for a key K in a map M can be obtained with M[K]; looking up a non-existent key is a fatal error.
4.2 A pure PEG version of Warth et al.’s proposal
While Warth et al.’s approach is given in terms of Packrat parsing, there is nothing about the approach which makes it fundamentally incompatible with a pure PEG approach—Packrat memoisation is used to detect left-recursion and (in an adaption over the original Packrat definition) to store the growing seed. In this section I show that by using a simple map to record left-recursive seed growth, one can provide a pure PEG analogue of the Packrat approach, without the (conceptual and storage) overhead of total memoisation.
2: function apply-rule(R, P, Rorig, Porig)
3: if R = Rorig ∧ P ∈ growing[R] then
4: return growing[R][P]
5: else if R = Rorig ∧ P = Porig then
6: growing[R][P] ← null
7: while true do
8: result ← apply-rule(R, P, Rorig, Porig)
9: seed ← growing[R][P]
10: if result = null ∨ (seed ≠ null ∧ result.pos < seed.pos) then
11: remove P from growing[R]
12: return seed
13: end if
14: growing[R][P] ← result
15: end while
16: else
17: traditional PEG rule application
18: end if
19: end function
Algorithm 1 is written in the style of Warth et al.’s direct left-recursion Packrat algorithm and shows my adaption of their algorithm for ‘pure’ PEGs. While Warth et al.’s algorithm is presented as an APPLY-RULE function which evaluates a rule R at position P, the pure PEG APPLY-RULE takes two extra arguments Rorig and Porig; these are used so that the ‘calling’ rule can transmit to APPLY-RULE its identity (Rorig) and the input position in effect when Rorig was called (Porig where Porig ≤ P). APPLY-RULE returns a result object on a successful parse; unsuccessful parses return null. The precise details of a result are left largely abstract; I assume only that it has a pos attribute which details its finishing input position. Using this algorithm, we can now use the left-recursive PEG from Section 3 to parse the input 1-2-3, receiving a parse tree equivalent to the input (((1)-2)-3). Happily, Algorithm 1 not only terminates (left-recursion does not cause infinite loops), but, for this example, correctly retains the expected left-associativity of the original PEG (a standard tree walker over this parse tree will return the answer -4).
While Algorithm 1 can initially appear rather confusing, in reality it is relatively simple. First of all, we can assume that all aspects of PEG parsing other than rule calling are exactly as they would be in ‘normal’ PEG evaluation. Where Warth et al.’s proposal uses memoisation to detect that left-recursion is occurring, Algorithm 1 instead uses a map:
- growing is a map <R → <P → seed >> from rules to maps of input positions to seeds at that input position. This is used to record the ongoing growth of a seed for a rule R at input position P.
growing is the data structure at the heart of the algorithm. A programming language-like type for it would be Map<Rule,Map<Int,Result>>. Since we statically know all the rules for a PEG, growing is statically initialised with an empty map for each rule at the beginning of the algorithm (line 1).
Algorithm 1’s simplest case is when Rorig is not left-recursively calling itself, in which case rule calling happens as normal (line 17). However, if the call of R is left-recursive then there are two cases. It is most natural to consider the second case first, which is when left-recursion has not yet occurred but is about to begin. This is triggered when no input has been consumed, detected when P has not advanced over Porig (line 5). We first set the seed in the growing map at input position P for rule R to null (line 6), meaning that if / when left-recursion happens for this rule at this input position, the initial left-recursion will fail. For a rule like expr <- expr "-" num / num, this means that, on the first recursive call, the first part of the ordered choice will fail (due to line 4), forcing the ordered choice to then try its second part.
The while loop starting at line 7 is where the algorithm changes from a standard recursive top-down PEG parser to a Warth et al. iterative bottom-up parser. In essence, we continually re-evaluate the rule R at input position P (note that P does not advance); each time this re-evaluation is successful, we update the seed in growing[R][P] (line 14). As expected, this means that each update of the seed includes within it the previous seed.
Re-evaluation of R at input position P can be unsuccessful for two reasons: if the re-evaluation fails completely; or if the result returned by re-evaluation consumes less of the input than the current seed (if one exists). The former case is trivial (though note that, by definition, it can only trigger on the first attempt at left-recursion). The latter is less obvious, and is not explained in depth by Warth et al. Intuitively, if a left-recursive call returns a ‘shorter’ result than the previous known one, then by definition it has not used the current seed; in other words, the left-recursion must have exhausted itself. When re-evaluation is unsuccessful, the seed is returned (line 12). Note that the seed will be null if the left-recursive call failed to match any input.
The final unexplained part of Algorithm 1 is when left-recursion is occurring and R calls itself. In this case, the seed is immediately returned (line 4). Note again that this will be set to null (due to line 6) on the first left-recursive call; thus line 4 is the key point in preventing an infinite loop in the presence of left-recursion.
4.3 Practical considerations
Algorithm 1 is tailored for simplicity of presentation and analysis, not for performance. In particular, it imposes unnecessary checks for all rules. A practical implementation can trivially reduce this cost by statically determining which rules are potentially directly left-recursive, and only calling the APPLY-RULE in Algorithm 1 for those rules; normal rules can call a ‘traditional’ PEG function which does not impose such costs. Thus, non-left-recursive calls can trivially be relieved of any extra processing over-head related to left-recursion handling. Furthermore, many approaches will (for efficiency reasons) fold APPLY-RULE into a larger function. In such cases, the third and fourth arguments – Porig and Rorig – are also trivially removed. The example implementation that accompanies this paper (see Section 7) demonstrates both aspects.
5 Incorrect parses with Algorithm 1
Unfortunately Algorithm 1 has a major, if subtle, flaw. Consider the following minor change to Warth et al.’s example from Section 3, where the right hand side of the first ordered choice in expr has been changed from num to expr:
When we use this PEG and Algorithm 1 to parse the input 1-2-3 we get a parse tree tree equivalent to the input (1-(2-(3))). As with traditional left-recursion removal, we have obtained a right-associative parse. It may not be immediately obvious that this is an incorrect result: after all, if the above PEG were a CFG, this would be an entirely valid parse. However this parse violates a fundamental aspect of PEGs, which is that rules match text greedily in order to ensure unambiguous parses [3]. In the above right-associative parse, one can clearly see that the right-recursive call of expr has matched more text (2-3) than the left-recursive call (1).
An important question is whether this error is a result of the move from Packrat parsing to pure PEGs in Algorithm 1. However exactly the same issue can be seen in faithful implementations of Warth et al.’s algorithm. Given the following PEG, Warth’s OMeta tool [11] produces exactly the same incorrect parse for 1-2-3:
Expr = Expr:l "-" Expr:r -> (l - r)
| Num:i -> (i),
Num = digit+:ds -> ds.join(’’)
}
A corollary of greedy matching is that, for a given input position P, later expressions in an ordered choice cannot affect the text matched by earlier expressions (i.e. in e0/e1, e1 cannot effect e0). Algorithm 1 also violates this corollary. Consider the PEG:
When used to parse 1+2-3 the resulting parse tree is equivalent to the input (((1)+2)-3). If we now make the second part of the ordered choice in expr right-recursive:
then the parse of 1+2-3 creates a parse tree equivalent to the input (1+(2-(3))). In other words, the change to the second expression of the ordered choice has effected the first expression.
As these examples show, by unintentionally altering the rules on greedy matching, Algorithm 1 introduces ambiguity into PEG parsing, thus losing many of the formal guarantees of traditional PEGs. To the best of my knowledge, this is the first time that this problem with Warth et al.’s algorithm has been shown.
6 Identifying and mitigating the problem
Let us first consider the simplest solution to the problem in Section 5: disallowing all (direct or indirect) recursive calls in a left-recursive rule other than an initial left-recursive call.  Restricting the set of parseable PEGs to this is trivial – and easily done dynamically; static enforcement would require some cunning as we shall soon see – and will clearly solve the problem. However, while it does increase the set of parseable PEGs relative to the traditional approach, it is still rather restrictive.
In this section, I first show that right-recursion in left-recursive rules is the fundamental problem, allowing a wider class of PEGs to be parsed; by forbidding these, Algorithm 1 can be safely used (Section 6.1). I then show that all definitely direct left-recursive rules without potential right-recursion can be safely parsed too (Section 6.3).
6.1 Pinpointing right-recursion in left-recursive rules
An intuitive analysis of the examples in Section 5 and Algorithm 1 highlights the underlying problem. While Algorithm 1 contains both a top-down and bottom-up parser, the two are not on an equal footing—the top-down parser gets the first opportunity to run to completion. When the bottom-up parser calls APPLY-RULE on line 8 of Algorithm 1, it allows the top-down parser opportunity to match the maximum possible text without giving the bottom-up parser an opportunity to wrest back control. Thus in any battle between left and right-recursion in a single rule, right-recursion (using the top-down parser) always wins over left-recursion (using the bottom-up parser).
That rules that are both left and right-recursive is the underlying issue can be seen with a minor modification to the running example, placing an arbitrary input consuming ‘end marker’ after the right-recursive call to expr (thus making it merely recursive, not right-recursive):
If we now parse 1-2m-3m (i.e. our running example text with end markers inserted), we get a parse tree equivalent to the input (((1)-2)-3)—the end markers have prevented right-recursion matching an arbitrary amount of text, thus allowing left-recursion to work as expected, giving us a left-associative parse.
It thus seems that we can now state a fairly liberal, but still easily implementable, solution which prevents the problem noted in Section 5 from arising: PEGs which contain rules which are both left and right recursive are not permitted. However there is one further subtlety which we must consider, which is best highlighted by the following modification to the running example, making end markers optional:
If we parse the text 1-2-3 against this PEG, we will once again receive an incorrect right-associative parse. Until now I have implicitly assumed that right-recursive rules are those which ‘look obviously’ right-recursive. In fact, some rule calls are right-recursive, or merely recursive, depending on the input string and whether they are followed by other expressions which can match against the empty string; the same idea is also true for left-recursion. We can use the standard nullables computation from parsing theory [5], which is trivially transferable to PEGs, to statically calculate potentially left or potentially right-recursive calls1.
Therefore a precise, fairly liberal, solution to the problem noted in Section 5 is to statically forbid any potentially left-recursive rule which contains potentially right-recursive calls. Non-left-recursive rules can still safely include potentially (direct or indirect) right-recursive calls.
6.2 The case for right-recursion in left-recursive rules
In Section 6.1 I showed that forbidding potentially right-recursive calls in potentially left-recursive rules solved the problem of Section 5. For many uses, this may well allow a sufficiently large class of PEGs to be parsed. There are, however, two reasons why we might wish to remove this restriction. First, this sort of (seemingly arbitrary) restriction is arguably one of the reasons why parsing is considered a black art by many otherwise capable people; the classically liberally minded have every right to ask whether we can allow some form of right-recursion in left-recursive rules. Second, grammars with both left and right recursion are the most natural way to write and evolve many grammars; their absence can complicate development.
For example, a standard expression grammar encoding correct precedences for +, -, *, and - looks as follows in traditional PEGs:
Term <- Fact "+" Term / Fact "-" Term / Fact
Fact <- Int "*" Fact / Int "/" Fact / Int
Int <- [0-9]+
Using Warth et al.’s algorithm we can rewrite this to be left-recursive as follows:
Term <- Term "+" Fact / Term "-" Fact / Fact
Fact <- Fact "*" Int / Fact "/" Int / Int
Int <- [0-9]+
Although this rewriting has simply swapped the left and right-recursion, many people find the left-recursive version more natural. However, in both of the above grammars, we are forced to be careful about making sure the recursion terminates consistently: in both the Term and Fact rules, note that the non-recursive alternative at the end of the rules (Fact and Int respectively) has to be encoded in each of the preceding alternatives too. This means that some seemingly local alterations to the grammar in fact require much larger chunks to be altered. For example, if we wish to allow bracketed expressions in the above grammar, we not only have to add a new rule, but change three references to it, as shown in the following (where changes are highlighted in italics):
Term <- Term "+" Fact / Term "-" Fact / Fact
Fact <- Fact "*" Prim / Fact "/" Prim / Prim
Prim <- "(" Term ")" / Int
Int <- [0-9]+
Consider if, instead, we had been able to write the original expression grammar using both left and right-recursion:
Term <- Term "+" Term / Term "-" Term / Fact
Fact <- Fact "*" Fact / Fact "/" Fact / Int
Int <- [0-9]+
Adding bracketed expressions to this grammar would then require changing only a single reference:
Term <- Term "+" Term / Term "-" Term / Fact
Fact <- Fact "*" Fact / Fact "/" Fact / Prim
Prim <- "(" Term ")" / Int
Int <- [0-9]+
Though there are other arguments one could advance for justifying why right-recursion in left-recursive rules is useful, I hope that the above example of increased ease of grammar evolution provides sufficient motivation.
6.3 Allowing direct definite right-recursion in left-recursive rules
In order to begin tackling the problem of allowing right-recursion in left-recursive rules, we first have to solve a challenge: can we ensure that the top-down parser gets sufficient opportunity to run when right-recursion is present in left-recursive rules? A simple observation is key: when we are in left-recursion and then encounter right-recursion, the bottom-up parser allows the right-recursion to continue many recursive levels deep, gobbling up text. Instead, we want the right-recursion to go only one recursive level deep, and then to return control to the bottom-up parser.
The solution, at a high level, therefore seems reasonably straight forward. When we are in left-recursion on a rule R and then right-recursively call R, we need to make all further right recursive calls to R fail, until the top-level right-recursion has returned control to the bottom-up parser. Intuitively, for a rule such as expr <- expr "+" expr / num, the right-recursive call of expr would then match only num, since the first part of the ordered choice would fail.
At a low level, we are forced to consider the issue of potential right-recursion. The high-level solution implicitly relies on statically identifying right-recursive calls; however when executed, potential right-recursion, by definition, may sometimes be simply normally-recursive or definitely right-recursive. To make matters worse, we can not dynamically determine whether a potentially right-recursive call is definitely right-recursive or not until its containing rule is completely evaluated. With PEGs this is not very useful since – unlike traditional CFGs, where one can backtrack arbitrarily – PEGs commit themselves to certain parses which backtracking can not undo. The issue can be seen in this extension of expr:
Given this PEG, we would hope that 1-2-3 would parse as (((1)-2)-3). In order for this to be the case, we would first need to evaluate the sub-expression ("-" expr)? to determine whether it matched any of the input; depending on whether it did, or did not, we would then know whether to treat the middle call of expr as normal recursion (not needing special treatment) or right recursion (and requiring the special rule mentioned in the high-level solution). Since we can not evaluate ("-" expr)? before evaluating the middle call of expr, this means that the high-level solution outlined above would mean the above PEG is ambiguous, since the potential right-recursion could lead to two different parses.
The problem with the high-level solution and potential right-recursion seems inherent. The only way to avoid it is to ban potential right-recursion in left-recursive rules. For the avoidance of doubt, we can state the following two rules: non-left recursive rules are allowed to have potential right-recursion; but left-recursive rules must either have no right-recursion or definite right-recursion.
There is one last issue to consider: potential left-recursion. As with right-recursion, one can create rules such as e <- e? "x" e where left-recursion occurs, or not, solely depending on the input. One could therefore wait until run-time to discover whether a rule is called left-recursively, and, if the rule was also statically determined to be potentially right-recursive, complain. The algorithm in the following section supports this dynamic approach. The accompanying implementation, on the other hand, recognises that this is unlikely to be the desired behaviour, and statically rejects potentially right-recursive calls in potentially left-recursive rules.
6.4 An algorithm for definite right-recursion in left-recursive rules
2: limit ←{}
3: function apply-rule(R, P, Rorig, Porig)
4: if R = Rorig ∧ R.drr then
5: if R ∈ limit then
6: return null
7: else if Porig ∈ growing[R] then
8: add(limit, R)
9: traditional PEG rule application
10: del(limit, R)
11: else
12: traditional PEG rule application
13: end if
14: else if R = Rorig ∧ P ∈ growing[R] then
15: return growing[R][P]
16: else if R = Rorig ∧ P = Porig then
17: growing[R][P] ← null
18: while true do
19: result ← apply-rule(R, P, Rorig, Porig)
20: seed ← growing[R][P]
21: if result = null ∨ (seed ≠ null ∧ result.pos < seed.pos) then
22: remove P from growing[R]
23: return seed
24: end if
25: growing[R][P] ← result
26: end while
27: else
28: if R ∈ limit then
29: del(limit, R)
30: traditional PEG rule application
31: add(limit, R)
32: else
33: traditional PEG rule application
34: end if
35: end if
36: end function
Algorithm 2 shows, in the style of Warth et al., how PEGs can cope with both direct left-recursion and direct definite right-recursion. Presenting the algorithm in this fashion does involve some compromises; however, the advantage conferred by continuity outweighs the distaste incurred by ugliness. In particular, there is a need to convey the information about whether a rule call is right-recursive or not. For the sake of simplicity, I assume that the R argument to the APPLY-RULE function has a boolean attribute drr (Definitely Right-Recursive) which is set to true if that rule is definitely right-recursive.
The first thing to note about Algorithm 2 is that it is really just an extension of Algorithm 1—lines 14–26 inclusive (the bottom-up parser) are unchanged.
The most complex part of Algorithm 2 relates to calling a right-recursive rule (lines 5–13). We maintain a set limit which contains the rule names of all current right-recursive calls (note that limit is global only to maintain symmetry with growing; it could as easily be a fifth argument to APPLY-RULE). Therefore when calling a definitely right-recursive call R, we first need to see whether we’re already in right-recursion on R; if so we fail (line 6), in order to ensure that right-recursion never goes more than one level deep. If we’re not in right-recursion on a rule, but are in left-recursion (which is implied if Porig is in growing[R], meaning that the bottom-up parser is in operation), then we add R to limit and evaluate the rule as normal (lines 8–10). If we’re not in right or left-recursion, we evaluate the rule purely as normal (line 12).
The final subtlety in Algorithm 2 relates to when right-recursion is happening in a rule R and we non-right-recursively call R. In this case, we need to ensure that the non-right-recursive call of R evaluates as normal, without limiting recursion to just one level. Fortunately we do not need to be as explicit as this might suggest; it is sufficient to temporarily remove R from limit (if it exists) whenever R is called and it does not fit into the cases listed above (lines 28–34).
Given the following PEG and the string 1-2-3 we get the left-associative parse we originally desired:
7 Conclusions
This paper first presented a ‘pure PEG’ adaption of Warth et al.’s left-recursive Packrat algorithm. I then showed cases where this algorithm failed, before identifying a safe subset of left-recursive PEGs which can be used with this algorithm. I then extended the algorithm, allowing left-recursive rules with definite right-recursion to work as expected. As this paper has shown, in order to safely parse right-recursive PEGs, a number of subtle issues need to be considered, and the class of right-recursive PEGs safely parseable is smaller than might originally have been hoped for. While the obvious next step is to extend the solutions presented in this paper to deal with indirect left and indirect right recursion, this may well prove to be quite challenging and to impose further restrictions on valid PEGs. This therefore raises a deeper philosophical question: are PEGs really suited to allowing left-recursion? That question is left to others to ponder.
Example code which implements the algorithms in this paper can be found
at
http://tratt.net/laurie/research/pubs/files/direct_left_recursive_pegs.
My thanks to Tony Sloane for verifying the problem noted in Section 5 with the Packrat implementation in his Kiama tool and providing comments on a draft of this paper; to David Hazell, Franco Raimondi, and Chris Hyuck for providing comments on a draft of this paper; and to Alessandro Warth for answering questions. Any errors and infelicities are my own.
References
[1] J. Earley. An efficient context-free parsing algorithm. Communications of the ACM, 13(2), Feb. 1970.
[2] B. Ford. Packrat parsing: Simple, powerful, lazy, linear time. In International Conference on Functional Programming, pages 36–47, Oct. 2002.
[3] B. Ford. Parsing expression grammars: a recognition-based syntactic foundation. In Proc. POPL, pages 111–122. ACM, Jan. 2004.
[4] R. Grimm. Better extensibility through modular syntax. volume 14 of Proc. Programming language design and implementation, pages 38–51, June 2006.
[5] D. Grune and C. J. Jacobs. Parsing techniques. 1998.
[6] R. R. Redziejowski. Some aspects of parsing expression grammar. Fundamenta Informaticae, 85(1-4):441–451, 2008.
[7] C. Seaton. A programming language where the syntax and semantics are mutable at runtime. Master’s thesis, University of Bristol, May 2007.
[8] L. Tratt. Domain specific language implementation via compile-time meta-programming. TOPLAS, 30(6):1–40, 2008.
[9] E. Visser. Scannerless generalized-LR parsing. Technical Report P9707, Programming Research Group, University of Amsterdam, July 1997.
[10] A. Warth, J. Douglass, and T. Millstein. Packrat parsers can support left recursion. In Proc. PEPM, pages 103–110. ACM, Jan. 2008.
[11] A. Warth and I. Piumarta. OMeta: an object-oriented language for pattern matching. In Proc. Dynamic Languages Symposium, pages 11–19. ACM, 2007.


{% set active = "rooms" -%} {% extends "base.html" -%} {% from "macros.jinja" import render_card -%} {% block content -%}


TWEB Github Analytics
Home
(current)
About
Application
Contact
[TWEB] Github Analytics
Analyze the habits of a given user
Click on your target:
zhaar
Thuy-My
Roldak
ounon
dbnsky
sydneyhauke
wasadigi
FredericJacobs
odersky
XTBoris
Analyze a user
You chose to go over
with a fine-tooth comb! Here are the result :
Let us see this first chart...
Now for the second chart...


September, 2012
Originally published at brownconversation.org
Dear Freshman,
Welcome to the greatest university-college on Earth! I’m glad you made it–not everybody does. But you did. You accomplished what probably seemed like your sole life goal for the last four (or maybe more) years. You got into a great college–no wait, scratch that, I said the greatest.
So now that you’re going to Brown, you might be wondering: “Now what?” Unfortunately, the first thing you’ll learn at Brown is that nobody can answer that for you. Actually, you won’t “learn” that in the conventional sense of the word. If you’re like me, or like many other Brown students I know, you’ll spend at least the first year or two here asking everyone you meet, “Now what?”, and never getting a straight answer. Of course, you won’t just ask them outright, but you’ll come up with your own ways.
To your advisor, you’ll ask: “What courses should I take?” To your friends1, you’ll ask: “What do you want to do after college?” To your family, you’ll ask: “How is everything back home?”
And that last one doesn’t sound particularly related to the question of “Now what?”, but trust me, nostalgia is one of the first warning signs that you might be suffering from the affliction that we all share here: the plague of the plan-less.
Of course, in theory, you should already know what you’re doing here. In your application to Brown, you had to answer: “Why do you want to go to college?” But in that context your answer was probably a bit biased. You knew who would be reading it, and you had to show off your good side. Since you got in, you were probably smart enough to not just say, “Because my parents wanted me to,” or, “Because I can get a higher paying job if I have a degree.” But those are both reasons that a lot of people decide to go to college. I won’t judge them as “good” or “bad” reasons, but they are valid reasons. So now that you’re off the hook–now that nobody’s watching–why don’t you try again?
Exercise 1: Answer truthfully: Why do you want to go to college?
Oh, you didn’t know there would be exercises? Well I don’t expect you to just accept everything I say as gospel, so I’m adding in exercises so you can prove (or disprove) my arguments to yourself.2 I strongly urge you to write down the answers, both for posterity and because writing is a great way to organize your thoughts.3
If you were able to complete the first exercise, then I’m very impressed. When I first got to Brown, I don’t think I really knew why I was here. If you feel like I did, that’s okay. Let me repeat that: it’s okay to not know why you’re going to college. Or at least, it’s okay for now. But you should try to figure it out, because when you’re facing the abyss that is the Open Curriculum, you’ll want to know what your goals are.
In the coming days–and years–you’re going to have to make a lot of decisions. If you want to make good decisions, you need to know what you’re trying to achieve with your decisions.
Exercise 2: Prove or disprove the following theorem: Making good decisions requires well-defined goals.
What do I mean when I talk about all these important decisions? Well, here are some examples of decisions I’ve had to make since I started Brown:
These are all pretty practical academic decisions, but some have been more subtle, such as:
Yes, believe it or not, decisions about your social life also depend on what your goals are at Brown.
You might be thinking, “I faced all these same decisions in high school. What’s so different about college?” Well in high school, you had at least one well-defined goal: to get into college. In college, the next step is much more hazy, as is the causal connection between your actions now and your future pursuits.
You could say that the next step for all of us is to get a job, since that’s how one makes money to buy food and shelter. But just like going to college wasn’t the only option after high school, getting a job and making money isn’t the only option after college.  Or you could move back in with your parents. Or you could travel.
If you do manage to decide on one of these goals, it’s still not clear what you need to do in college to achieve that goal. College does not work the same way as high school. You don’t necessarily get where you want to go by getting good grades and accumulating activities on your résumé.
Exercise 3: Prove or disprove the following theorem: You won’t get a job after college by getting good grades. Then prove or disprove this corrollary: You won’t achieve your goals by getting good grades.
Now, I don’t expect that you can give a very strong proof for or against this theorem. At this point in your life, you don’t have enough information to support a statement like this. I don’t either. But the fundamental assumption up to this point in your life, through all of schooling so far, is that the way to achieve your goals is to get good grades.
This principle may still apply for you in college. But it might not. The most important thing to realize is that it does not necessarily apply in college just because it applied in high school. If it does apply, you will need to rejustify it with new evidence.
How can you go about getting this evidence? Who has the information that can justify the way you approach college? I’ve been trying to answer these questions for a while. I’ve tried talking to professors, deans, older students, alumni, and employers, among others. Each person I talk to adds a new perspective, a couple more pixels that help define my image of how the world after college works.
As an example of where this search might take you, I’ll tell you some of the conclusions I’ve reached for myself. Of course, these don’t apply to every Brown student. Some of my closest friends are following principles that are in some ways the polar opposites of mine.
I decided that, for me, getting good grades is not the way to achieve my goals for college (one of which is getting a job afterwards). It took me two years at Brown to come to this conclusion, but once I did, I stopped taking my courses for letter grades, and started using the Satisfactory/No Credit option exclusively. I don’t calculate a GPA to put on my résumé.4
My goal in telling you this is not to inspire you to do the same. I haven’t even given you any justification for my decision. My reasons are complicated enough that it wouldn’t be worth it to write them out here.5 The reason I’m telling you my story is to show you that it’s possible to decide at the end of Exercise 3 that the principles from high school don’t apply for you in college.
When I first got to Brown, my freshman advisor gave me advice about courses and grades: that I should take courses in a variety of subjects; that I should take one course S/NC per semester (which I should use to explore new subjects); and that I should always take courses in my concentration for letter grades. This seems to be the standard wisdom at Brown. In fact, you can find these same recommendations in the section on Grade Options in your “Guide to Brown” booklet. For some students, this may be a very good strategy. Of course, it all depends on your goals.
This strategy did not line up with my goals. However, since nobody told me that I could have my own strategy that corresponded with my own goals, I followed the advice I was given without questioning if it was appropriate for me.
Of course, nobody forced me to continue following this path, and when I decided it wasn’t for me, I was able to change. This is what makes Brown so special. Nobody will stop you from doing what you want to do. The curricular structures here are incredibly flexible. However, nobody but you knows what you want to do. So if you ask others what to do, they will never be able to give you a complete answer. They can help–and their help will be indispensible–but ultimately you have to take responsibility for your own path.
Exercise 4: Building on your results from Exercise 1, come up with a plan for your academic life at Brown, including strategies for picking courses and choosing grade options.
This last exercise is a monumental task. You’ll probably end up changing your answer multiple times during your time at Brown. But that’s ok, because even if it seems intimidating, there are a lot of resources that you can use to help gather information and clarify your thoughts. Instead of trying to give you all my personal opinions and advice (like I haven’t already!), here are some places you can go to try to answer some of the questions I’ve raised here:
The Curricular Resource Center
Run by students, the CRC’s goal is to help students “fully engage with Brown’s curriculum and utilize its academic resources.” Here you’ll find other students who know all the tricks to navigating the Open Curriculum. Find more info at the CRC website.
The Brown Conversation
The Brown Conversation is a weekly dinner discussion group for students who are interested in thinking deeply about both the questions raised in this letter as well as anything else related to education at Brown. Drop in for a conversation and a delicious meal, or check out the Brown Conversation website.
Me
How rude of me not to introduce myself. My name’s Jonah. I’m a senior at Brown, class of 2013. If you have questions about my experience at Brown, want advice, or just want to complain about your roommate, I’d love to chat with you. Just shoot me an email at<EMAIL_ADDRESS>I wish you luck with your first year here. And once again, welcome to the greatest university-college on Earth. It really is the greatest. Trust me, you’ll see.
Sincerely,
Jonah Kagan
P.S. If you don’t mind sharing your answers to the exercises, I’d be very interested to see what you wrote.
Yes, don’t worry, you’ll make friends.↩
If you disprove them, I’d love to hear how.↩
See Paul Graham’s “The Age of the Essay”↩
Fun fact: Brown doesn’t calculate official GPAs either. If you decide you want to have a GPA, you have to do it yourself.↩
If you’re interested in knowing, just ask. I like to talk about it.↩
Thoughts? Am I crazy?
Let me know: jonahkagan@gmail.com


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 34 35
36 package ti.sysbios.hal;
37
38 import xdc.runtime.Error;
39
40 /*!
41 * ======== Hwi ========
42 * Hardware Interrupt Manager Proxy.
43 *
44 * This module provides APIs for managing hardware interrupts.
45 * These APIs are generic across all supported targets and devices
46 * and should provide sufficient functionality for most applications.
47 *
48 * The actual implementations of the Hwi module APIs are
49 * provided by the Hwi module delegates.
50 * Additional, family-specific Hwi module APIs may also be provided by
51 * the Hwi module delegates.
52 * See the list of
53 * {@link ./../family/doc-files/delegates.html Delegate Mappings}
54 * to determine which Hwi delegate is used
55 * for your target/device.
56 *
57 * You can statically or dynamically assign functions that run when
58 * specific hardware interrupts occur. Dynamic assignment of Hwi
59 * functions to interrupts at run-time is done
60 * using the {@link #create Hwi_create} function.
61 *
62 * Interrupt routines can be written completely in C, completely in
63 * assembly, or in a mix of C and assembly. In order to support interrupt
64 * routines
65 * written completely in C, an interrupt dispatcher is provided that performs
66 * the requisite prolog and epilog for an interrupt routine.
67 *
68 * Some routines are assigned to interrupts by the other SYS/BIOS
69 * modules. For example, the
70 * {@link ti.sysbios.knl.Clock} module configures its own timer interrupt
71 * handler.
72 *
73 * @a(constraints)
74 * Since the hal Hwi module has no knowledge of the delegate Hwi
75 * module's instance definition, Hwi_construct() can NOT be properly
76 * supported.
77 *
78 * If {@link ti.sysbios.BIOS#runtimeCreatesEnabled BIOS.runtimeCreatesEnabled}
79 * is set to true, both Hwi_create() and Hwi_construct()
80 * will attempt to dynamically create (ie NOT construct) a delegate Hwi
81 * object.
82 *
83 * If {@link ti.sysbios.BIOS#runtimeCreatesEnabled BIOS.runtimeCreatesEnabled}
84 * is set to false, both Hwi_create() and Hwi_construct() will fail.
85 *
86 * @p(html)
87 *
 Calling Context

88 *

89 * <colgroup span="1"></colgroup> <colgroup span="5" align="center">
90 * </colgroup>
91 *
92 *
 Function 	 Hwi 	 Swi
93 *  Task 	 Main 	 Startup

94 *
95 *
 {@link #clearInterrupt} 	 Y 	 Y
96 *  Y 	 Y 	 Y

97 *
 {@link #create} 	 N 	 N
98 *  Y 	 Y 	 N

99 *
 {@link #disable} 	 Y 	 Y
100 *  Y 	 Y 	 Y

101 *
 {@link #disableInterrupt} 	 Y 	 Y
102 *  Y 	 Y 	 N

103 *
 {@link #enable} 	 Y 	 Y
104 *  Y 	 N 	 N

105 *
 {@link #enableInterrupt} 	 Y 	 Y
106 *  Y 	 Y 	 N

107 *
 {@link #Params_init} 	 Y 	 Y
108 *  Y 	 Y 	 Y

109 *
 {@link #restore} 	 Y 	 Y
110 *  Y 	 Y 	 Y

111 *
 {@link #restoreInterrupt} 	 Y 	 Y
112 *  Y 	 Y 	 Y

113 *
 {@link #construct} 	 N 	 N
114 *  Y 	 Y 	 N

115 *
 {@link #delete} 	 N 	 N
116 *  Y 	 Y 	 N

117 *
 {@link #destruct} 	 N 	 N
118 *  Y 	 Y 	 N

119 *
 {@link #getHookContext} 	 Y 	 Y
120 *  Y 	 Y 	 N

121 *
 {@link #setFunc} 	 Y 	 Y
122 *  Y 	 Y 	 N

123 *
 {@link #setHookContext} 	 Y 	 Y
124 *  Y 	 Y 	 N

125 *
 Definitions:

126 *

127 *
 Hwi: API is callable from a Hwi thread.

128 *
 Swi: API is callable from a Swi thread.

129 *
 Task: API is callable from a Task thread.

130 *
 Main: API is callable during any of these phases:

131 *

132 *
 In your module startup after this module is started
133 * (e.g. Hwi_Module_startupDone() returns TRUE).

134 *
 During xdc.runtime.Startup.lastFxns.

135 *
 During main().

136 *
 During BIOS.startupFxns.

137 *

138 *
 Startup: API is callable during any of these phases:

139 *

140 *
 During xdc.runtime.Startup.firstFxns.

141 *
 In your module startup before this module is started
142 * (e.g. Hwi_Module_startupDone() returns FALSE).

143 *

144 *

145 *

146 *
147 *

148 * @p
149 *
150 * @a(Runtime Hwi Creation)
151 *
152 * Below is an example that configures an interrupt at runtime.
153 * Typically such code would be placed in main().
154 *
155 * @p(code)
156 * #include <xdc/runtime/Error.h>
157 * #include <ti/sysbios/hal/Hwi.h>
158 *
159 * Hwi_Handle myHwi;
160 *
161 * Int main(Int argc, char* argv[])
162 * {
163 * Hwi_Params hwiParams;
164 * Error_Block eb;
165 *
166 * Hwi_Params_init(&hwiParams);
167 * Error_init(&eb);
168 *
169 * // set the argument you want passed to your ISR function
170 * hwiParams.arg = 1;
171 *
172 * // set the event id of the peripheral assigned to this interrupt
173 * hwiParams.eventId = 10;
174 *
175 * // don't allow this interrupt to nest itself
176 * hwiParams.maskSetting = Hwi_MaskingOption_SELF;
177 *
178 * //
179 * // Configure interrupt 5 to invoke "myIsr".
180 * // Automatically enables interrupt 5 by default
181 * // set params.enableInt = FALSE if you want to control
182 * // when the interrupt is enabled using Hwi_enableInterrupt()
183 * //
184 *
185 * myHwi = Hwi_create(5, myIsr, &hwiParams, &eb);
186 *
187 * if (Error_check(&eb)) {
188 * // handle the error
189 * }
190 * }
191 *
192 * Void myIsr(UArg arg)
193 * {
194 * // this runs when interrupt #5 goes off
195 * }
196 * @p
197 *
198 * @a(Hook Functions)
199 *
200 * Sets of hook functions can be specified for the Hwi module
201 * using the configuration tool. Each set contains these hook
202 * functions:
203 * @p(blist)
204 * -Register: A function called before any statically-created Hwis
205 * are initialized at runtime. The register hook is called at boot time
206 * before main() and before interrupts are enabled.
207 * -Create: A function that is called when a Hwi is created.
208 * This includes hwis that are created statically and those
209 * created dynamically using {@link #create Hwi_create}.
210 * -Begin: A function that is called just prior to running a Hwi.
211 * -End: A function that is called just after a Hwi finishes.
212 * -Delete: A function that is called when a Hwi is deleted at
213 * run-time with {@link #delete Hwi_delete}.
214 * @p
215 *
216 * Register Function
217 *
218 * The Register function is provided to allow a hook set to store its
219 * hookset ID. This id can be passed to
220 * {@link #setHookContext Hwi_setHookContext} and
221 * {@link #getHookContext Hwi_getHookContext} to set or get
222 * hookset-specific context. The
223 * Register function must be specified if the hook implementation
224 * needs to use {@link #setHookContext Hwi_setHookContext} or
225 * {@link #getHookContext Hwi_getHookContext}.
226 * The registerFxn hook function is called during system initialization
227 * before interrupts have been enabled.
228 *
229 * @p(code)
230 * Void myRegisterFxn(Int id);
231 * @p
232 *
233 * Create and Delete Functions
234 *
235 * The create and delete functions are called whenever a Hwi is created
236 * or deleted. They are called with interrupts enabled (unless called
237 * at boot time or from main()).
238 *
239 * @p(code)
240 * Void myCreateFxn(Hwi_Handle hwi, Error_Block *eb);
241 * @p
242 *
243 * @p(code)
244 * Void myDeleteFxn(Hwi_Handle hwi);
245 * @p
246 *
247 * Begin and End Functions
248 *
249 * The beginFxn and endFxn function hooks are called with interrupts
250 * globally disabled, therefore any hook processing function will contribute
251 * to the overall system interrupt response latency. In order to minimize
252 * this impact, carefully consider the processing time spent in an Hwi
253 * beginFxn or endFxn function hook.
254 *
255 * @p(code)
256 * Void myBeginFxn(Hwi_Handle hwi);
257 * @p
258 *
259 * @p(code)
260 * Void myEndFxn(Hwi_Handle hwi);
261 * @p
262 *
263 * Hook functions can only be configured statically.
264 *
265 */
266
267 @ModuleStartup
268
269 module Hwi inherits ti.sysbios.interfaces.IHwi
270 {
271 /*!
272 * Error raised when a stack overflow (or corruption) is detected.
273 *
274 * This error is raised by kernel's stack checking function. This
275 * function checks the stacks before every task switch to make sure
276 * that reserved word at top of stack has not been modified.
277 *
278 * The stack checking logic is enabled by the {@link #checkStackFlag}
279 * configuration parameter. If this flag is set to true, the kernel will
280 * validate the stacks.
281 */
282 config Error.Id E_stackOverflow = {
283 msg: "E_stackOverflow: ISR stack overflow."
284 };
285
286 /*!
287 * Initialize ISR stack with known value for stack checking at runtime
288 *
289 * This is also useful for inspection of stack in debugger or core
290 * dump utilities for stack overflow and depth.
291 *
292 * Default is true.
293 * (see {@link #checkStackFlag}).
294 */
295 metaonly config Bool initStackFlag = true;
296
297 /*!
298 * Check for Hwi stack overrun during Idle loop.
299 *
300 * If true, then an idle function is added to the idle loop
301 * that checks for a Hwi stack overrun condition and raises
302 * an Error if one is detected.
303 *
304 * The check consists of testing the top of stack value against
305 * its initial value (see {@link #initStackFlag}). If it is no
306 * longer at this value, the assumption is that the ISR stack
307 * has been overrun. If the test fails, then the
308 * {@link #E_stackOverflow} error is raised.
309 *
310 * Runtime stack depth computation is only performed if {@link #initStackFlag} is
311 * also true.
312 *
313 * Default is true.
314 * (see {@link #initStackFlag}).
315 *
316 * To enable or disable full stack checking, you should set both this
317 * flag and the {@link ti.sysbios.knl.Task#checkStackFlag}.
318 */
319 metaonly config Bool checkStackFlag = true;
320
321 /*!
322 * ======== disable ========
323 * Globally disable interrupts.
324 *
325 * Hwi_disable globally disables hardware interrupts and returns an
326 * opaque key indicating whether interrupts were globally enabled or
327 * disabled on entry to Hwi_disable().
328 * The actual value of the key is target/device specific and is meant
329 * to be passed to Hwi_restore().
330 *
331 * Call Hwi_disable before a portion of a function that needs
332 * to run without interruption. When critical processing is complete, call
333 * Hwi_restore or Hwi_enable to reenable hardware interrupts.
334 *
335 * Servicing of interrupts that occur while interrupts are disabled is
336 * postponed until interrupts are reenabled. However, if the same type
337 * of interrupt occurs several times while interrupts are disabled,
338 * the interrupt's function is executed only once when interrupts are
339 * reenabled.
340 *
341 * A context switch can occur when calling Hwi_enable or Hwi_restore if
342 * an enabled interrupt occurred while interrupts are disabled.
343 *
344 * Hwi_disable may be called from main(). However, since Hwi interrupts
345 * are already disabled in main(), such a call has no effect.
346 *
347 * @a(constraints)
348 * If a Task switching API such as
349 * {@link ti.sysbios.knl.Semaphore#pend Semaphore_pend()},
350 * {@link ti.sysbios.knl.Semaphore#post Semaphore_post()},
351 * {@link ti.sysbios.knl.Task#sleep Task_sleep()}, or
352 * {@link ti.sysbios.knl.Task#yield Task_yield()}
353 * is invoked which results in a context switch while
354 * interrupts are disabled, an embedded call to
355 * {@link #enable Hwi_enable} occurs
356 * on the way to the new thread context which unconditionally re-enables
357 * interrupts. Interrupts will remain enabled until a subsequent
358 * {@link #disable Hwi_disable}
359 * invocation.
360 *
361 * Swis always run with interrupts enabled.
362 * See {@link ti.sysbios.knl.Swi#post Swi_post()} for a discussion Swis and
363 * interrupts.
364 *
365 * @b(returns) opaque key for use by Hwi_restore()
366 */
367 @Macro
368 override UInt disable();
369
370 /*!
371 * ======== enable ========
372 */
373 @Macro
374 override UInt enable();
375
376 /*!
377 * ======== restore ========
378 */
379 @Macro
380 override Void restore(UInt key);
381
382 /*!
383 * ======== viewGetHandle ========
384 * @_nodoc
385 * Returns the corresponding hal Hwi handle for a delegate Hwi handle
386 *
387 * @b(returns) hal Hwi handle
388 */
389 metaonly Handle viewGetHandle(Ptr pi);
390
391 /*!
392 * ======== viewGetLabel ========
393 * @_nodoc
394 * Returns the corresponding hal Hwi label for a delegate Hwi handle
395 *
396 * @b(returns) hal Hwi Label
397 */
398 metaonly String viewGetLabel(Ptr pi);
399
400 instance:
401
402 /*!
403 * ======== create ========
404 * Create a dispatched interrupt.
405 *
406 * To cause a C function to run in response to a particular system
407 * interrupt, you create a Hwi object that encapsulates information
408 * regarding the interrupt required by the Hwi module.
409 *
410 * The standard static and dynamic forms of the "create" function are
411 * supported by the ti.sysbios.hal.Hwi module.
412 * The following C code configures interrupt 5 with the "myIsr"
413 * C function.
414 *
415 * @p(code)
416 * #include <ti/sysbios/hal/Hwi>
417 *
418 * Hwi_create(5, myIsr, NULL, NULL);
419 * @p
420 *
421 * The NULL, NULL arguments are used when the default instance
422 * parameters and generic error handling is satisfactory for creating
423 * a Hwi object.
424 *
425 * A Hwi dispatcher table entry is created and filled with the
426 * function specified by the fxn parameter and the attributes
427 * specified by the params parameter.
428 *
429 * If params is NULL, the Hwi's dispatcher properties are assigned a
430 * default set of values. Otherwise, the following properties
431 * are specified by a structure of type Hwi_Params.
432 *
433 * @p(blist)
434 * - The arg element is a generic argument that is passed to the plugged
435 * function as its only parameter. The default value is 0.
436 * - The enableInt element determines whether the interrupt should be
437 * enabled in the IER by create.
438 * - The maskSetting element defines the dispatcherAutoNestingSupport
439 * behavior of the interrupt.
440 * @p
441 *
442 * Hwi_create returns a pointer to the created Hwi object.
443 *
444 * @param(intNum) interrupt number
445 * @param(hwiFxn) pointer to ISR function
446 */
447 override create(Int intNum, FuncPtr hwiFxn);
448
449 /*!
450 * ======== getHookContext ========
451 * Get hook instance's context for a Hwi.
452 *
453 * The Handle passed to this API must be the handle passed
454 * to any of the Hook functions, not the one returned by
455 * {@link #create Hwi_create}.
456 *
457 * @b(returns) hook instance's context for hwi
458 */
459 override Ptr getHookContext(Int id);
460
461 /*!
462 * ======== setHookContext ========
463 * Set hook instance's context for a Hwi.
464 *
465 * The Handle passed to this API must be the handle passed
466 * to any of the Hook functions, not the one returned by
467 * {@link #create Hwi_create}.
468 *
469 * @param(id) hook instance's ID
470 * @param(hookContext) value to write to context
471 */
472 override Void setHookContext(Int id, Ptr hookContext);
473
474 internal:
475
476
477 metaonly config UInt numHooks = 0;
478
479 /*! target/device-specific Hwi implementation. */
480 proxy HwiProxy inherits ti.sysbios.interfaces.IHwi;
481
482 struct Instance_State {
483 HwiProxy.Handle pi;
484 };
485 }


Drip Drip DropA simple, fast, Tiny Tiny RSS reader.
Built by Chris Grindstaff - Inspiration from Lim Chee Aun's HackerWebApp


Calvin Klein
Toggle navigation
CALVIN KLEIN COLLECTION
|
WOMEN
MEN
JEANS
UNDERWEAR
SHOES + ACCESSORIES
FRAGRANCE
HOME
SALE
|
FILMS
TIMELINE
Step 1
Step 2
Step 3
CHOOSE YOUR COLLECTION
CHOOSE YOUR SILHOUETTE
CHANGE LOCATION:
DESIGN CONCEPT ONLY
ABOUT CALVIN KLEIN
|
FIND A STORE


|
Dear {{.name}}:
|
|
Your booking for interview has been changed by our recruitment team. The new booked time slot is {{.week}}, {{.time}}.
|
|
You will need to log in to our application portal to start the online interview.
|
|
|
Log in
|
Best regards,
DaDaABC Teacher Recruitment Team.
|
|


[jsTree][jstree] is a tree view for jQuery (depends on 1.9.1 or later). It is absolutely free (MIT licence) and supports all modern browsers and IE from version 8 up. jsTree can display trees by parsing HTML or JSON and supports AJAX, it is themeable and easy to configure and customize. Events are fired when the user interacts with the tree. Other notable features are inline editing, drag’n’drop support, fuzzy searching (with optional server side calls), tri-state checkbox support, configurable node types, AMD compatibility, easily extendable via plugins. [jstree]: http://www.jstree.com/
Download or checkout the latest copy and include the scripts and styles in your web page. Then create an instance (in this case using the inline HTML).
```html <div id="container">
</div> ```
Listen for changes on the tree using events:
html
<script>
$(function () {
$('#container').on('changed.jstree', function (e, data) {
console.log(data.selected);
});
});
</script>
And interact with the tree:
html
<script>
$(function () {
$('#container').jstree(true).select_node('child_node');
});
</script>
For a complete list of configuration options, events and available functions refer to the [documentation][docs] and [demos][demo]. [docs]: http://jstree.com/docs [demo]: http://jstree.com/demo
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using grunt.
Please do NOT edit files in the “dist” subdirectory as they are generated via grunt. You’ll find source code in the “src” subdirectory!
If you want to you can always [donate a small amount][paypal] to help the development of jstree. [paypal]: https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=paypal@vakata.com¤cy_code=USD&amount=&return=http://jstree.com/donation&item_name=Buy+me+a+coffee+for+jsTree
Copyright (c) 2014 Ivan Bozhanov (http://vakata.com)
Licensed under the [MIT license][mit]. [mit]: http://www.opensource.org/licenses/mit-license.php


psml module¶
a library for generating OpenSCAD 3D models
https://www.github.com/wovo/psml
(c) Wouter van Ooijen<EMAIL_ADDRESS>Distributed under the Boost Software License, Version 1.0.
This is a Python library (Python 3 required) for writing 3D model code that can be rendered and processed by OpenSCAD.
The library has a vector class. A vector holds x, y and (optional) z numeric values. A vector is used to specify (in 2D or 3D) a size, a location, or a displacement, or sometimes just to hold 2 or 3 values. A vector can be created from two or three values. Two vectors can be added or subtracted. A vector can multiplied with or divided by a numeric value. When a function requires a vector, it can in most cases be specified either by a vector value, or as 2 or 3 individual numeric parameters.
This library creates and manipulates 3D solid objects. The image below shows the basic solid objects: box, cylinder, cone, and sphere.
Basic flat (2D) objects are rectangle, circle, polygon, and text, as shown in the next image.
A flat object can be extended into a solid object by extruding it (in the z direction), while optionally twisting it around the z axis in the process. The cross shown below was extruded and twisted while it was at the origin. The right circle was extruded and twisted while it was just to the right of the origin.
Objects and be added, subtracted and intersected with the operators +, - and *. The image below shows two separate cylinders, the addition of these two cylinders, the second cylinder subtracted from the first one, and the intersection of the two.
Manipulators can be applied to an object with the ** (power) operator. Basic manipulators are vector, rotate, mirror, scale and resize. The example below show an object unchanged, , shifted up, rotated along the x axis, mirrored in the y-z plane (note the eyes), scaled (by 2 in the y and z directions), and resized (to fit in a cube).
The repeat2, repeat4 and repeat8 manipulators repeat their subject. Repeat2 does this at the original location, and shifted by the specified vector. Repeat4 does this at the 4 corners of the rectangle specified by the vector. Repeat8 does this at the 8 corners of the box specified by the vector.
The negative manipulator creates a dominant emptiness from its subject. The image below shows at the left the addition of two normal pipes. The result is not a usable pipe crossing because the walls of each pipe will block the other pipe. In the middle it shows the addition of two pipes of which the empty interior is dominant. This produces a ‘successful’ pipe crossing. But this also removes part of the vertical axle. At the right the crossing was first made positive, which reduces the dominant emptiness to an normal, and then the vertical axle was added.
In the code examples solid objects are created. What is not show is that such an object must be written to a file to be processed by OpenSCAD, using a write() call.
This documentation is meant to be usable on its own, without having to read the OpenSCAD documentation, hence it is worded as if the library provides all the functionality, while in fact it is in most cases just a thin layer on top of the OpenSCAD language.
Some OpenSCAD features are not directly available in the library. To compensate, a solid can be created from a string, which is passed directly to OpenSCAD. Likewise, a manipulator can be constructed from a lambda, which gets the string representation of its subject as parameter.
The library has type hints. The examples use typeguard to check these hints.
-
psml.
apply
(text: str, subject: Optional[shape]) → Optional[shape]¶ apply an OpenSCAD operation to a shape
- Parameters
text – the text of the OpenSCAD operation
subject – the shape to which the operation is applied
This function applies an OpenSCAD operation to a shape. This can be useful for OpenSCAD operations that are not otherwise available in the library.
For convenience, single quotes in the text are replaced by double quotes (OpenSCAD uses double quotes).
Coloring and translation are available in the library, but could be done with OpenSCAD functions as shown in the example.
apply( "translate([10,0,0]) color('red')", box( 5, 10, 3 ) )
-
psml.
back
(v: float) → psml.vector¶ vector that shifts back (away from you along the y axis) by v
- Parameters
v – the shift distance
Return vector( 0, v, 0 ): a vector with y set to v, and x and z to 0.
-
psml.
box
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None, rounding: float = 0) → psml.shape¶ box shape
- Parameters
x – x size, or a vector which specifies all 3 sizes
y – y size (omit when x is a vector)
z – z size (omit when x is a vector)
rounding – rounding radius (default: no rounding)
The size of the box can be specified either by three values x, y and z, or by a vector. The box has its lower-left corner at the origin. The rounding specifies the radius of the rounding at the corners and edges. The default rounding is 0, which yields sharp boundaries.
box( 20, 30, 10 )
box( vector( 20, 30, 10 ), rounding = 2 )
-
psml.
circle
(*, radius: Optional[float] = None, diameter: Optional[float] = None, facets: Optional[float] = None) → psml.shape¶ circle shape
- Parameters
radius – the radius of the circle
diameter – the diameter of the circle
facets – number of circle facets
The size of the circle is specified by either its radius or its diameter. The circle is in the x-y plane, with its center at the origin. Optionally, the number of circle facets can be specified. The default is the global variable number_of_circle_facets.
circle( radius = 20 )
circle( radius = 20, facets = 5 )
-
psml.
color
(r: Union[float, vector], g: Optional[float] = None, b: Optional[float] = None, alpha: float = 1.0)¶ a color in RGB format
- Parameters
r – the r of the vector, or the full color vector
g – (optional) the g of the color
b – (optional) the b of the color
alpha – (optional) the alpha value (opacity)
The color is either specified as a single vector parameter, or as separate r, g and b values. When a single vector is specified, and alpha value (if present) must be named parameter.
The individual color values must be in the range 0..255.
An alpha of 0 is full transparency, a value of 1 is a solid color. A lower alpha makes the object more faintly colored. It does not make it opaque (translucent).
Colors are visible in OpenSCAD preview, but NOT in after rendering. Hence the examples below show previews, unlike the other examples, which show the result of rendering.
color( 0, 255, 255 ) ** sphere( radius = 10 ) + right( 30 ) ** color( 0, 255, 255, 0.5 ) ** sphere( radius = 10 ) + back( 20 ) ** color( vector( 255, 0, 255 ), alpha = 1 ) ** box( 50, 10, 10 )
The color names in the World Wide Web consortium’s SVG color list are available, both with PascalCase and in lowercase.
MediumVioletRed ** sphere( radius = 10 ) + right( 30 ) ** blue ** sphere( radius = 10 ) + right( 60 ) ** mediumspringgreen ** sphere( radius = 10 )
-
psml.
cone
(height: Union[float, vector, None] = None, *, radius1: Optional[float] = None, diameter1: Optional[float] = None, radius2: Optional[float] = None, diameter2: Optional[float] = None, facets: Optional[float] = None) → psml.shape¶ cone shape
- Parameters
height – the height of the cone, or a vector that specifies the height and the two radiuses
radius1 – the radius of the cone at its bottom,
radius2 – the radius of the cone at its top
diameter1 – the diameter of the cone at its bottom,
diameter2 – the diameter of the cone at its top
facets – number of circle facets
The size of the cone is specified by its height, its the radius or diameter at its base, and its radius or diameter at its top.
Optionally, the number of circle facets can be specified. The default is the global variable number_of_circle_facets.
cone( radius1 = 10, radius2 = 0, height = 20 ) \ + right( 35 ) ** cone( vector( 20, 20, 10 ))
-
psml.
cylinder
(height: Union[float, vector, None] = None, *, radius: Optional[float] = None, diameter: Optional[float] = None, rounded_top: bool = False, facets: Optional[float] = None) → psml.shape¶ cylinder shape
- Parameters
height – the height of the cylinder, or a vector that specifies the height and the radius
radius – the radius of the cylinder
diameter – the diameter of the circle
rounded_top – whether the top is rounded (default: not)
facets – number of circle facets
The size of the cylinder is specified by its the radius at its base, and its height.
Optionally, the number of circle facets can be specified. The default is the global variable number_of_circle_facets.
cylinder( radius = 10, height = 20 ) \ + right( 35 ) ** cylinder( vector( 10, 20 ))
-
psml.
down
(v: float) → psml.vector¶ vector that shifts down (along the z axis) by v
- Parameters
v – the shift distance
Return vector( 0, 0, -v ): a vector with z set to v, and x and y to 0.
-
psml.
dup2
(v: float) → psml.vector¶ vector( v, v )
- Parameters
v – the value for x and y
Return a vector with both x and y set to v.
-
psml.
dup3
(v: float) → psml.vector¶ vector( v, v, v )
- Parameters
v – the value for x, y and z
Return a vector with x, y and z set to v.
-
psml.
extrude
(height: float, twist: float = 0, scale: float = 1, facets: Optional[int] = None)¶ extrude operator: extend a 2d object in the z direction
- Parameters
height – the height over which the object will be extruded
twist – the degrees over which the object is rotated along the z axis over its extrusion height (default: 0)
scale – the scaling determines the relative size of the object at its maximum extrusion height
facets – number of steps used in the extrusion
Optionally, the number of steps can be specified. The default is the global variable number_of_extrude_facets.
-
psml.
facets
(numer_of_facets: int) → None¶ accuracy (number of facets) of circles, spheres and fonts
The default setting (32) is a compromise between speed and accuracy.
For quick rendering of complex designs a lower value (10, or even 5) might be appropriate.
This function has effect on shapes that are created after its call, so better call it before you create any elements.
cylinder( radius = 10, height = 20 ) \ + vector( 25, 0, 10 ) ** sphere( radius = 10 )
facets( 9 ) cylinder( radius = 10, height = 20 ) \ + vector( 25, 0, 10 ) ** sphere( radius = 10 )
-
psml.
front
(v: float) → psml.vector¶ vector that shifts to the front (towards you along the y axis) by v
- Parameters
v – the shift distance
Return vector( 0, -v, 0 ): a vector with y set to -v, and x and z to 0.
-
psml.
hollow_box
(size: psml.vector, walls, rounding=0) → psml.shape¶ a hollow box
- Parameters
size – size of the box (x, y, z )
walls – wall thickness
rounding – rounding diameter (default: no rounding)
This is a hollow project enclosure box, Use screw_and_nut_column() to place screw holes and recesses. Use split() to separate it into a top and a bottom part. (Or use project_enclosure() which does these things for you.)
-
psml.
hull
= <psml.modifier object>¶ convex hull
This manipulator creates the convex hull around its subject, which can be 2D or 3D.
x = sphere( radius = 10 ) + vector( -3, -3, 15 ) ** box( 6, 6, 6 ) x + right( 30 ) ** hull ** x
-
psml.
identity
= <psml.vector object>¶ modifier that doesn’t change its subject
-
psml.
left
(v: float) → psml.vector¶ vector that shifts left (along the x axis) by v
- Parameters
v – the shift distance
Return vector( -v, 0, 0 ): a vector with x set to -v, and y and z to 0.
-
class
psml.
m_screw
(diameter, thread)¶ Bases:
object
a metric (m3 etc.) screw
-
psml.
minkowski
= <psml.modifier object>¶ minkowski sum
This modifier computes the Minkowski sum of two or more 2D or 3D objects. It must be applied to a sum of objects. When it is applied to something else (for instance the result of applying a modifier) it is a no-op.
-
psml.
mirror
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None)¶ mirror operator: mirror an object in a planes
- Parameters
x – the x of the vector, or the full vector
y – (optional) the y of the vector
z – (optional) the z of the vector
This modifier mirrors its subject in a plane through the origin. The plane is specified by its normal vector (the vector that is perpendicular to the mirror plane).
The vector is either specified as a single vector parameter, or as separate x, y and z values.
The example shows a text, the same text mirrored in the y-z plane, mirrored in the x-z plane, and mirrored in the x-y plane.
x = extrude( 4 ) ** text( "Hello", height = 10 ) x + right( 70 ) ** mirror( 1, 0, 0 ) ** x + right( 80 ) ** mirror( 0, 1, 0 ) ** x + right( 120 ) ** mirror( 0, 0, 1 ) ** x
-
class
psml.
modifier
(function)¶ Bases:
object
modifier that can be applied by **
A modifier is an object that can be applied to its subject, a shape, using the ** operator. A modifier is constructed by providing the function that is to be applied.
The subject can be None instead of a shape, in which case the result will also be None.
right10 = modifier( lambda s: s + vector( 0, 0, 10 ) ** s ) right10 ** sphere( radius = 6 )
-
psml.
negative
= <psml.modifier object>¶ makes its subject a dominant negative
This manipulator makes its subject a dominant negative: something that will not be filled.
-
psml.
polygon
(points: Iterable[Union[vector, Tuple[float, float]]]) → psml.shape¶ polygon shape
- Parameters
points – a list of 2d vectors or value pairs
A polygon is defined by the list of its edge points. Each edge point can be specified by either a vector or a pair of values.
polygon( [ [ 0, 0 ], [ 3, 0 ], [ 2, 1 ], [ 2, 2 ], [ 3, 2 ], [ 1, 3 ], [ 0, 3 ], [ 1, 1 ] ] )
-
psml.
positive
= <psml.modifier object>¶ removes dominant negatives
The positive manipulator subtracts and removes the dominant emptinesses in its subject, so an solid can be placed in the space of what was a dominant emptiness.
-
psml.
project_enclosure
(size, walls, rounding=0)¶ a simple 2-part project enclosure
- Parameters
size – outer size of the enclosure (x, y, z )
walls – wall thickness
rounding – rounding diameter (default: no rounding)
This is a simple 2-part project enclosure box.
-
psml.
rectangle
(x: Union[float, vector], y: Optional[float] = None, rounding: float = 0) → psml.shape¶ rectangle shape
- Parameters
x – x size, or a vector which specifies both sizes
y – y size (omit when x is a vector)
rounding – rounding radius (default: no rounding)
The size of the rectangle can be specified either by two values x and y, or by a vector. The rectangle has its lower-left corner at the origin. The rounding specifies the radius of the rounding at the corners and edges. The default rounding is 0, which yields sharp boundaries.
rectangle( vector( 20, 30 ), rounding = 0 ) # or rectangle( 20, 30 )
rectangle( 20, 30, 3 ) # or rectangle( shift( 20, 30 ), rounding = 3 )
-
psml.
repeat2
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None)¶ repeat at two positions
- Parameters
x – the x of the vector, or the full vector
y – (optional) the y of the vector
z – (optional) the z of the vector
The vector is either specified as a single vector parameter, or as separate x, y and z values.
This manipulator repeats its subject twice: once at its original location, and once shifted by the specified vector.
repeat2( 10, 20, 10 ) ** cylinder( radius = 5, height = 10 )
-
psml.
repeat4
(x: Union[float, vector], y: Optional[float] = None)¶ repeat at four positions
- Parameters
x – the x of the vector, or the full vector
y – (optional) the y of the vector
The vector is either specified as a single vector parameter, or as separate x and y values.
This manipulator repeats its subject at the four corners of the rectangle specified by the parameters.
repeat4( 10, 20 ) ** box( 5, 5, 5 )
-
psml.
repeat8
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None)¶ repeat at eight positions
- Parameters
x – the x of the vector, or the full vector
y – (optional) the y of the vector
z – (optional) the z of the vector
The vector is either specified as a single vector parameter, or as separate x, y and z values.
This manipulator repeats its subject at the corners of the box specified by the parameters.
repeat8( 10, 20, 8 ) ** sphere( radius = 3 )
-
psml.
resize
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None)¶ resize operator: resize an object
- Parameters
x – the x of the vector, or the full vector
y – (optional) the y of the vector
z – (optional) the z of the vector
The vector is either specified as a single vector parameter, or as separate x, y and z values.
This manipulator resizes its subject to the sizes indicated for the x, y and z direction. A size of 0 keeps the size (in that direction) unchanged. A size of None scales the size in that direction with another non-0 non-None size.
The example below shows a text, and the same text scaled to fit in a 30 by 10 rectangle.
t = text( "hello world" ) t + back( 10 ) ** resize( 30, 10 ) ** t
The example below shows a sphere, and the same sphere scaled to size 40 in the x direction, unchanged (size 10) ijn the y direction, and z matching the x direction (scaled to 40).
s = sphere( radius = 10 ) s + right( 40 ) ** resize( 40, 0, None ) ** s
-
psml.
right
(v: float) → psml.vector¶ vector that shifts right (along the x axis) by v
- Parameters
v – the shift distance
Return the vector( v, 0, 0 ): a vector with x set to v, and y and z to 0.
-
psml.
rotate
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None)¶ rotate operator: rotate an object around one or more axises
- Parameters
x – the x of the vector, or the full vector
y – (optional) the y of the vector
z – (optional) the z of the vector
The vector is either specified as a single vector parameter, or as separate x, y and z values.
This manipulator rotates its object along one or more axises, by the specified amount in degrees.
The example below shows an object, and the same objects rotated 45 degrees, first along the x axis, then along the y axis, and lastly along the z axis.
c = cylinder( radius = 10, height = 30 ) + vector( -10, -20, 0 ) \ ** box( 20, 20, 5 ) m = ( c + right( 30 ) ** rotate( 45, 0, 0 ) ** c + right( 60 ) ** rotate( 0, 45, 0 ) ** c + right( 100 ) ** rotate( 0, 0, 45 ) ** c )
-
psml.
scale
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None)¶ scale operator: scale an object in one or more directions
- Parameters
x – the x of the vector, or the full vector
y – (optional) the y of the vector
z – (optional) the z of the vector
The vector is either specified as a single vector parameter, or as separate x, y and z values.
This manipulator scales its subject by the factors indicated for the x, y and z direction. A factor of 1 retains the original size, 2 makes it twice at large, etc.
The example below shows a box, and the same box scaled by 2 in the x direction, scaled by 2 in the y direction, and scaled by 2 in the z direction.
b = box( 10, 20, 40 ) m = ( b + right( 30 ) ** scale( 2, 1, 1 ) ** b + right( 70 ) ** scale( 1, 2, 1 ) ** b + right( 100 ) ** scale( 1, 1, 2 ) ** b )
-
psml.
screw_and_nut_column
(height, screw: psml.m_screw, wall=1) → psml.shape¶ a screw and nut column
- Parameters
height – height of the column
srew – screw (specifies diameter and thread lenghth)
wall – wall thickness (default 1mm)
This is a vertical screw-and-nut column for keeping two parts of an enclosure together with a flat screw and a hex nut. It is assumed to be spliced into the top and bottom parts.
-
class
psml.
shape
(positive: str, negative: str = '')¶ Bases:
object
2D or 3D shape
Shapes can be added, subtracted or intersected by using the +, - or * operators.
-
write
(file_name='output.scad')¶ write the shape to the specified file
- Parameters
file_name – name of the file
This function prints the OpenSCAD representation of the shape to the indicated file (default: output.scad). That file can be opened in OpenSCAD for visualization or export as to a .stl file.
If the file_name does not contain a “.” the suffix “.scad” is appended.
# these lines have the same effect sphere( 10 ).write() sphere( 10 ).write( "output" ) sphere( 10 ).write( "output.scad" )
-
-
psml.
sphere
(radius: Optional[float] = None, diameter: Optional[float] = None, facets: Optional[float] = None) → psml.shape¶ sphere shape
- Parameters
radius – the radius of the sphere
diameter – the diameter of the sphere
facets – number of sphere facets
The size of the sphere is specified by either its radius or its diameter.
Optionally, the number of sphere facets can be specified. The default is the global variable number_of_sphere_facets.
sphere( 10 ) \ + ( right( 20 ) ** sphere( radius = 6 ) ) \ + ( right( 35 ) ** sphere( radius = 4 ))
-
psml.
split_box
(b, s, h, d=<psml.vector object>)¶ split an enclosure in top and bottom parts
This function splices a box into separate top and bottom parts, which are placed next to each other (default: 5 mm apart in x direction).
- Parameters
b – the box to splice
s – the size of the box
h – height at which the box is spliced
d – distance between the parts
-
psml.
text
(txt: str, height: float = 5, facets: Optional[int] = None, args='') → psml.shape¶ text shape
- Parameters
txt – the text
height – the letter height
facets – number of facets used to draw the letters
args – extra arguments
Optionally, the number of facets can be specified. The default is the global variable number_of_text_facets.
text( "Hello world" )
Optionally, a string of extra arguments can be specified. Check the OpenSCAD documentation for the possible arguments. For your convenience, single quotes in the arg string are replaced by double quotes (OpenSCAD requires double quotes).
text( "Mary had a little lamb", facets = 5, args = "halign='center',valign='center'" )
The horizontal size of a text depends on the specified height the text itself, and on the font. This makes it difficult to predict the size of a text. The resize operator can be useful to scale a text to a know size.
-
psml.
up
(v: float) → psml.vector¶ vector that shifts up (along the z axis) by v
- Parameters
v – the shift distance
Return vector( 0, 0, v ): a vector with z set to v, and x and y to 0.
-
class
psml.
vector
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None)¶ Bases:
object
2d or 3d vector
This is a 2d (x,y) or 3d (x, y, z) vector.
A vector is used to denote a location, a displacement (shift) a size, or sometimes just 2 or 3 numeric values.
A vector has members x, y and z. For a 2d vector, the z value is None.
Vectors can be added or subtracted using the + or - operators. Vectors can be multiplied or divided by a scalar using the * and / operators.
-
x
= None¶ x (first) value of the vector
-
y
= None¶ y (second) value of the vector
-
z
= None¶ z (third) value of the vector
-
-
class
psml.
vector
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None) 2d or 3d vector
This is a 2d (x,y) or 3d (x, y, z) vector.
A vector is used to denote a location, a displacement (shift) a size, or sometimes just 2 or 3 numeric values.
A vector has members x, y and z. For a 2d vector, the z value is None.
Vectors can be added or subtracted using the + or - operators. Vectors can be multiplied or divided by a scalar using the * and / operators.
-
__init__
(x: Union[float, vector], y: Optional[float] = None, z: Optional[float] = None)¶ create from x and y, and an optional z value
Create a 2d vector from x and y values, or a 3d vector from x, y and z values.
When x is a vector, that vector is simply copied.
-
__add__
(rhs: psml.vector) → psml.vector¶ add two vector values (member-wise addition)
Adding two 2d vectors yields a 2d vector, adding two 3d vectors yields a 3d vector.
When a 2d vector and a 3d vector are added, the z value of the 2d vector is assumed to be 0.
-
__sub__
(rhs: psml.vector) → psml.vector¶ subtract two vector values (member-wise subtraction)
Subtracting two 2d vectors yields a 2d vector, subtracting two 3d vectors yields a 3d vector.
When a 2d vector and a 3d vector are subtracted, the z value of the 2d vector is assumed to be 0.
-
__mul__
(v: float) → psml.vector¶ multiply a vector by a scalar (member-wise multiplication)
-
__truediv__
(v: float)¶ divide a vector by a scalar (member-wise division)
-
__str__
() → str¶ convert to [ x, y ] or [ x, y, z ] string format
-
__pow__
(subject: Optional[shape]) → Optional[shape]¶ apply the vector to a shape
- Parameters
subject – the shape that is to be displaced (shifted)
A vector can be applied to a shape using the ** operator. This will displace (shift) the shape.
The subject can be None instead of a shape, in which case the result will also be None.
-
x
= None x (first) value of the vector
-
y
= None y (second) value of the vector
-


http://twitter.github.io/bootstrap/
http://ehealth.kcl.ac.uk/jerome/code-library/css/responsive/bootstrap/boilerplate/content/latest
Examples
SaIL Centres site (Wordpress theme)
http://ehealth.kcl.ac.uk/tel/pharmaceutical/az-project/mod3/content/hm-0200/hm-0200-100/latest/ (simple page)
http://ehealth.kcl.ac.uk/tel/pharmaceutical/az-project/mod3/content/hm-1100/hm-1100-100/latest/ (more complex with activity on page 4)
http://ehealth.kcl.ac.uk/tel/pharmaceutical/az-project/mod3/content/hm-1300/hm-1300-100/latest/ (activities 2)
Contact: jerome.dipietro@kcl.ac.uk


DfE enquiries
Central newsdesk 020 7783 8300
General enquiries 0370 000 2288
Press release
New research has shown the government’s £150 million a year PE and sport premium is helping to improve primary school sports provision.
Nine out of 10 primary schools have improved the quality of PE lessons thanks to the £150 million PE and sport premium, new research has shown.
The fund, introduced in 2013, goes directly to primary school headteachers who can use it however they want, to provide PE and sporting activities for pupils.
Today’s (9 September 2014) figures show that schools are getting real value for money, helping more children get active and healthy, and learning the confidence and skills that will help them to succeed in life.
Schools have used the money to recruit more PE teachers and sports specialists to improve the quality of lessons or after-school clubs, buy new equipment, offer a wider selection of sports and free after-school clubs.
The findings include:
Education Secretary Nicky Morgan and Children and Families Minister Edward Timpson joined Olympic gold medallist and London 2012 cycling star Victoria Pendleton for a visit to Bacons College, Rotherhithe, where they saw first-hand the benefits of competitive sport.
The group met children from local primary schools trying their hand at a range of sports before meeting some of Bacon College’s stars of the future - including the winners of the London Youth Games 4 x 100m relay, an aspiring Chelsea footballer and a nationally ranked badminton player.
Education Secretary Nicky Morgan said:
We want to create a world-class education system that gives children all the skills they need to succeed in modern Britain. Literacy and numeracy are part of that but confidence, discipline, and determination are equally important.
Some of those things can be taught in the classroom but the lessons children learn while playing sport, such as the importance of teamwork and the satisfaction of achieving a goal, are invaluable.
I’m delighted the PE and sport premium is having a positive effect on sports in our schools. Whether you’re an Olympic champion or a park runner, sport really can change people’s lives and I want everyone to take part.
Olympic gold medallist Victoria Pendleton said:
Developing a love of sport early on goes a long way to helping children develop confidence and competence.
Whether it’s a netball tournament between a few local schools or being part of an after-school football club, competitive sport really helps children learn resilience, team work, and builds character - not to mention the clear benefits for their health.
Sport at school shouldn’t be sidelined - which is why the extra funding primary schools are getting for sport will make such a valuable difference.
The PE and sport premium was launched in 2013 providing over £150 million a year to improve the quality of provision in every state primary school in England until the 2015 to 2016 financial year. The funding goes directly to primary school headteachers so that they can decide how best to use it to provide PE and sporting activities for pupils.
A typical primary school with 250 primary-age pupils this year received £9,250, the equivalent of around 2 days a week of a primary teacher’s or a coach’s time.
The fund has been used to:
The research report, published today (9 September, 2014), was based on a survey of primary schools between April and July 2014. It looked at how headteachers have spent the money and the impact of that spending on schools.
The full report is available on GOV.UK.
Central newsdesk 020 7783 8300
General enquiries 0370 000 2288
Published: 9 September 2014