| Classes_Details {methods} | R Documentation |
Class definitions are objects that contain the formal definition of a
class of R objects, usually referred to as an S4 class, to
distinguish them from the informal S3 classes.
This document gives an overview of S4 classes; for
details of the class representation objects, see help for the class
classRepresentation
.
When a class is defined, an object is stored that contains the
information about that class. The object, known as the
metadata defining the class, is not stored under the name of
the class (to allow programmers to write generating functions of
that name), but under a specially constructed name.
To examine the class definition, call getClass
. The
information in the metadata object includes:
The data contained in an object from an S4 class is defined by the slots in the class definition.
Each slot in an object is a component of the object;
like components (that is, elements) of a
list, these may be extracted and set, using the
function slot()
or more often the operator
"@"
. However, they
differ from list components in important ways.
First, slots can only be referred to by name, not by position,
and there is no partial matching of names as with list elements.
All the objects from a particular class have the same set of slot
names; specifically, the slot names that are contained in the
class definition. Each slot in each object always is an object
of the
class specified for this slot in the definition of the current class.
The word “is” corresponds to the R function of the same
name (is
), meaning that the class of the object in
the slot must be the same as the class specified in the
definition, or some class that extends the one in the
definition (a subclass).
A special slot name, .Data
, stands for the
‘data part’ of the object. An object from a class with a
data part is defined by specifying that the class contains one
of the R object types or one of the special pseudo-classes,
matrix
or array
, usually because the definition of
the class, or of one of its superclasses, has included the type
or pseudo-class in its contains
argument. A second
special slot name, .xData
, is used to enable inheritance
from abnormal types such as "environment"
See the section on inheriting from non-S4 classes
for details on the representation and
for the behavior of S3 methods with objects from these classes.
Some slot names correspond to attributes used in old-style S3
objects and in R objects without an explicit class, for
example, the names
attribute. If you define a class for
which that attribute will be set, such as a subclass of named
vectors, you should include "names"
as a slot. See the
definition of class "namedList"
for an example. Using the
names()
assignment to set such names will generate a
warning if there is no names slot and an error if the object in
question is not a vector type. A slot called "names"
can
be used anywhere, but only if it is assigned as a slot, not via
the default names()
assignment.
The definition of a class includes the superclasses —the
classes that this class extends. A
class Fancy
, say, extends a class Simple
if an
object from the Fancy
class has all the capabilities of
the Simple
class (and probably some more as well). In
particular, and very usefully, any method defined to work for a
Simple
object can be applied to a Fancy
object as
well.
This relationship is expressed equivalently by saying that
Simple
is a superclass of Fancy
, or that
Fancy
is a subclass of Simple
.
The direct superclasses of a class are those superclasses
explicitly defined. Direct superclasses can be defined in
three ways. Most commonly, the superclasses are listed in the
contains=
argument in the call to setClass
that creates the subclass. In this case the subclass will
contain all the slots of the superclass, and the relation
between the class is called simple, as it in fact is.
Superclasses can also be defined
explicitly by a call to setIs
; in this case, the
relation requires methods to be specified to go from subclass to
superclass. Thirdly, a class union is a superclass of all the
members of the union. In this case too the relation is simple,
but notice that the relation is defined when the superclass is
created, not when the subclass is created as with the
contains=
mechanism.
The definition of a superclass will also potentially contain its own direct superclasses. These are considered (and shown) as superclasses at distance 2 from the original class; their direct superclasses are at distance 3, and so on. All these are legitimate superclasses for purposes such as method selection.
When superclasses are defined by including the names of
superclasses in the contains=
argument to
setClass
, an object from the class will have all the
slots defined for its own class and all the slots defined
for all its superclasses as well.
The information about the relation between a class and a
particular superclass is encoded as an object of class
SClassExtension
. A list of such objects for
the superclasses (and sometimes for the subclasses) is included in
the metadata object defining the class. If you need to compute
with these objects (for example, to compare the distances), call
the function extends
with argument fullInfo=TRUE
.
The objects from a class created by a call to
new
are defined by the prototype object for the class and by
additional arguments in the call to new
, which are
passed to a method for that class for the function
initialize
.
Each class representation object contains a prototype object
for the class (although for a virtual class the prototype may be
NULL
). The prototype object must have values for all the
slots of the class.
By default, these are the prototypes of the corresponding slot
classes. However, the definition of the class can specify any
valid object for any of the slots.
There are a number of ‘basic’ classes, corresponding to the
ordinary kinds of data occurring in R. For example,
"numeric"
is a class corresponding to numeric vectors.
The other vector basic classes are "logical"
, "integer"
,
"complex"
, "character"
, "raw"
, "list"
and "expression"
.
The prototypes for
the vector classes are vectors of length 0 of the corresponding
type. Notice that basic classes are unusual in that the
prototype object is from the class itself.
In addition to the vector classes there are also basic classes
corresponding to objects in the
language, such as "function"
and "call"
.
These classes are subclasses of the virtual class "language"
.
Finally, there are object types and corresponding basic classes for
“abnormal” objects, such as "environment"
and
"externalptr"
.
These objects do not follow the
functional behavior of the language; in particular, they are not
copied and so cannot have attributes or slots defined locally.
All these classes can be used as slots or as superclasses for any other class definitions, although they do not themselves come with an explicit class. For the abnormal object types, a special mechanism is used to enable inheritance as described below.
A class definition can extend classes other than
regular S4 classes, usually by specifying them in the
contains=
argument to setClass
. Three groups
of such classes behave distinctly:
S3 classes, which must have been registered by a previous call to
setOldClass
(you can check that this has been done
by calling getClass
, which should return a class that
extends oldClass);
One of the R object types, typically a vector type, which then
defines the type of the S4 objects, but also a type such as
environment
that can not be used directly as a type
for an S4 object. See
below.
One of the pseudo-classes matrix
and array
, implying objects with
arbitrary vector types plus the dim
and dimnames
attributes.
This section describes the approach to combining S4 computations with older S3 computations by using such classes as superclasses. The design goal is to allow the S4 class to inherit S3 methods and default computations in as consistent a form as possible.
As part of a general effort to make the S4 and S3 code in R more
consistent, when objects from an S4 class are used as the first
argument to a non-default S3 method, either for an S3 generic function
(one that calls UseMethod
) or for one of the primitive
functions that dispatches S3 methods, an effort is made to provide a
valid object for that method. In particular, if the S4 class extends
an S3 class or matrix
or array
, and there is an S3
method matching one of these classes, the S4 object will be coerced to
a valid S3 object, to the extent that is possible given that there is
no formal definition of an S3 class.
For example, suppose "myFrame"
is an S4 class that includes the
S3 class "data.frame"
in the contains=
argument to
setClass
. If an object from this S4 class is passed to
a function, say as.matrix
, that has an S3 method for
"data.frame"
, the internal code for UseMethod
will convert the object to a data frame; in particular, to an S3
object whose class attribute will be the vector corresponding to the
S3 class (possibly containing multiple class names). Similarly for an
S4 object inheriting from "matrix"
or "array"
, the S4
object will be converted to a valid S3 matrix or array.
Note that the conversion is not applied when an S4 object is
passed to the default S3 method. Some S3 generics attempt to deal
with general objects, including S4 objects. Also, no transformation
is applied to S4 objects that do not correspond to a selected S3
method; in particular, to objects from a class that does not contain
either an S3 class or one of the basic types. See asS4
for the transformation details.
In addition to explicit S3 generic functions, S3 methods are defined for a variety of operators and functions implemented as primitives. These methods are dispatched by some internal C code that operates partly through the same code as real S3 generic functions and partly via special considerations (for example, both arguments to a binary operator are examined when looking for methods). The same mechanism for adapting S4 objects to S3 methods has been applied to these computations as well, with a few exceptions such as generating an error if an S4 object that does not extend an appropriate S3 class or type is passed to a binary operator.
The remainder of this section discusses the mechanisms for
inheriting from basic object types. See matrix
or array
for inhering from the matrix and array
pseudo-classes, or from time-series. For the
corresponding details for inheritance
from S3 classes, see setOldClass
.
An object from a class that directly and simply contains one
of the basic object types in R, has implicitly a corresponding
.Data
slot of that type, allowing computations to extract
or replace the data part while leaving other slots
unchanged. If the type is one that can accept attributes and is
duplicated normally, the inheritance also determines the type of the
object; if the class definition has a .Data
slot
corresponding to a normal type, the class of the
slot determines the type of the object (that is, the value of
typeof(x)
).
For such classes, .Data
is a pseudo-slot; that
is, extracting or setting it modifies the non-slot data in the
object. The functions getDataPart
and
setDataPart
are a cleaner, but essentially
equivalent way to deal with the data part.
Extending a basic type this way allows objects to
use old-style code for the corresponding type as well as S4
methods. Any basic type can be used for .Data
, but
a few types are treated differently because they do not behave like ordinary objects;
for example, "NULL"
, environments, and external pointers.
Classes extend these types by having a slot, .xData
,
itself inherited from an internally defined S4 class. This
slot actually contains an object of the inherited type, to
protect computations from the reference semantics of the type.
Coercing to the nonstandard object type then requires an
actual computation, rather than the "simple"
inclusion
for other types and classes. The intent is that programmers
will not need to take account of the mechanism, but one
implication is that you should not explicitly use the
type of an S4 object to detect inheritance from an arbitrary
object type. Use
is
and similar functions instead.
Chambers, John M. (2016) Extending R, Chapman & Hall. (Chapters 9 and 10.)
Methods_Details
for analogous discussion of methods,
setClass
for details of specifying class definitions,
is
,
as
,
new
,
slot


{% extends "story.html" %} {% block title %}Mission 3{% endblock %} {% block head %}{{ super() }}{% endblock %} {% block body %} {% block nav %} {{ super() }} {% endblock %}
There are a lot of useful resources under OMA’s navigation bar. Materials contain information about studies, student services, important dates and a lot more. Tools contain cloud services, MOT dictionaries, Turnitin and useful links, along with a list of other useful tools.
For your mission, go through all the drop-down menus and find out the place where you can confirm your autumn/spring attendance. Once done, mark yourself as complete.
{{ xp }}
Story progress


ONAMWAM - Overriding a Non-Abstract Method With an Abstract Method
Checks for the overriding of non-abstract methods by abstract methods in a subclass.
Wrong
void func () {}
}
abstract class Monkey extends Animal {
abstract void func ();
}
Tip: If it is a coincidence of names, rename your method. Otherwise, make this method abstract in an ancestor class or non-abstract in descendant.
Right
void func () {}
}
abstract class Monkey extends Animal {
abstract void extFunc ();
}


| PERLPLAN9(1) | Perl Programmers Reference Guide | PERLPLAN9(1) |
Some scripts, usually identified with a *.PL extension, are self-configuring and are able to correctly create their own shebang path from config information located in Plan 9 Perl. These you won't need to be worried about.
The functions not currently implemented include:
chown, chroot, dbmclose, dbmopen, getsockopt, setsockopt, recvmsg, sendmsg, getnetbyname, getnetbyaddr, getnetent, getprotoent, getservent, sethostent, setnetent, setprotoent, setservent, endservent, endnetent, endprotoent, umask
There may be several other functions that have undefined behavior so this list shouldn't be considered complete.
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, SIGPIPE, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGCHLD, SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU
This is a preliminary alpha version of Plan 9 Perl. Still to be implemented are MakeMaker and DynaLoader. Many perl commands are missing or currently behave in an inscrutable manner. These gaps will, with perseverance and a modicum of luck, be remedied in the near future.To install this software:
1. Create the source directories and libraries for perl by running the plan9/setup.rc command (i.e., located in the plan9 subdirectory). Note: the setup routine assumes that you haven't dearchived these files into /sys/src/cmd/perl. After running setup.rc you may delete the copy of the source you originally detarred, as source code has now been installed in /sys/src/cmd/perl. If you plan on installing perl binaries for all architectures, run "setup.rc -a".
2. After making sure that you have adequate privileges to build system software, from /sys/src/cmd/perl/5.00301 (adjust version appropriately) run:
mk install
If you wish to install perl versions for all architectures (68020, mips, sparc and 386) run:
mk installall
3. Wait. The build process will take a *long* time because perl bootstraps itself. A 75MHz Pentium, 16MB RAM machine takes roughly 30 minutes to build the distribution from scratch.
mk man
To begin your reading, start with:
man perl
This is a good introduction and will direct you towards other man pages that may interest you.
(Note: "mk man" may produce some extraneous noise. Fear not.)
Luther Huffman<EMAIL_ADDRESS>Strategic Computer Solutions, Inc.
| 2019-10-21 | perl v5.30.3 |


| Tests passed/Failed/Skipped: | 1/0/0 |
| Started on: | Mon Sep 24 12:13:22 EDT 2018 |
| Total time: | 80 seconds (80299 ms) |
| Included groups: | |
| Excluded groups: |
| PASSED TESTS | |||
| Test method | Exception | Time (seconds) | Instance |
| run_cukes Test class: runner_cukes.RunCukesTest Test method: Runs Cucumber Features Show output Show all outputs Feature File: features/GoogleFooterLinkTest.feature Feature: Google Footer links Checkup Scenario Outline: Selecting different footer links and checking the corresponding page When I select "<footerLink_text>" link in the Google footer (passed : 0s) Then I verified the selected Google page by the existence of the "<linkedPage_text>" text (undefined) Scenario Outline: Selecting different footer links and checking the corresponding page Given I launch the Google home page url (skipped) And I scroll down to the end of the page Bahar (skipped) When I select "Privacy" link in the Google footer (passed : 0s) Then I verified the selected Google page by the existence of the "Welcome to the Google Privacy Policy" text (undefined) Scenario Outline: Selecting different footer links and checking the corresponding page Given I launch the Google home page url (skipped) And I scroll down to the end of the page Bahar (skipped) Scenario Outline: Selecting different sublinks on the Google Privacy footer link When I select "Terms" link in the Google footer (passed : 0s) Then I verified the selected Google page by the existence of the "Google Terms of Service" text (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "<privacy_policy>" Bahar (undefined) Then I verified the selected "<policy_detail>" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Information we collect" Bahar (undefined) Then I verified the selected "We collect information" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "How we use information we collect" Bahar (undefined) Then I verified the selected "We use the information we collect" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Transparency and choice" Bahar (undefined) Then I verified the selected "People have different privacy concerns" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Information you share" Bahar (undefined) Then I verified the selected "Many of our services let you share information with others" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Accessing and updating your personal information" Bahar (undefined) Then I verified the selected "We aim to maintain our services in a manner that protects information" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Information we share" Bahar (undefined) Then I verified the selected "We do not share personal information with companies" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Information security" Bahar (undefined) Then I verified the selected "We work hard to protect Google and our users" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "When this Privacy Policy applies" Bahar (undefined) Then I verified the selected "Our Privacy Policy applies to all of the services offered by Google LLC" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Compliance and cooperation with regulatory authorities" Bahar (undefined) Then I verified the selected "We regularly review our compliance with our Privacy Policy" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Changes" Bahar (undefined) Then I verified the selected "Our Privacy Policy may change from time to time" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Specific product practices" Bahar (undefined) Then I verified the selected "The following notices explain specific privacy practices" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Other useful privacy and security related materials" Bahar (undefined) Then I verified the selected "Further useful privacy and security related materials can be found" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Self Regulatory Frameworks" Bahar (undefined) Then I verified the selected "Self Regulatory Frameworks" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Key terms" Bahar (undefined) Then I verified the selected "An affiliate is an entity that belongs" Policty detail Bahar (undefined) Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Privacy footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Partners" Bahar (undefined) Then I verified the selected "How Google uses data when you use our partners" Policty detail Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Terms footer link Given I launch the Google home page url (passed : 0s) And I scroll down to the end of the page Bahar (undefined) Scenario Outline: Selecting different sublinks on the Google Terms footer link Given I select the Privacy link in the Google footer Bahar (undefined) When I click on a specific Privacy Policy "Updates" Bahar (undefined) Then I verified the selected "We want to be as transparent as possible about the changes" Policty detail Bahar (undefined) Given I select the Terms link in the Google footer (undefined) When I click on a Technologies and Principles tab (passed : 0s) And I click on a specific Technologies "<Technologies>" (undefined) Scenario Outline: Selecting different sublinks on the Google Terms footer link Then I verified the selected "<Technology_detail>" Technology detail (undefined) Given I launch the Google home page url (undefined) And I scroll down to the end of the page Bahar (undefined) Given I select the Terms link in the Google footer (undefined) When I click on a Technologies and Principles tab (passed : 0s) And I click on a specific Technologies "Advertising" (undefined) Scenario Outline: Selecting different sublinks on the Google Terms footer link Then I verified the selected "Advertising keeps Google and many of the websites and services you use free of charge" Technology detail (undefined) Given I launch the Google home page url (undefined) And I scroll down to the end of the page Bahar (undefined) Given I select the Terms link in the Google footer (undefined) When I click on a Technologies and Principles tab (passed : 0s) And I click on a specific Technologies "How Google uses cookies" (undefined) Scenario Outline: Selecting different sublinks on the Google Terms footer link Then I verified the selected "A cookie is a small piece of text sent to your browser by a website you visit" Technology detail (undefined) Given I launch the Google home page url (undefined) And I scroll down to the end of the page Bahar (undefined) Given I select the Terms link in the Google footer (undefined) When I click on a Technologies and Principles tab (passed : 0s) And I click on a specific Technologies "How Google uses pattern recognition" (undefined) Scenario Outline: Selecting different sublinks on the Google Terms footer link Then I verified the selected "How Google uses pattern recognition to make sense of images" Technology detail (undefined) Given I launch the Google home page url (undefined) And I scroll down to the end of the page Bahar (undefined) Given I select the Terms link in the Google footer (undefined) When I click on a Technologies and Principles tab (passed : 0s) And I click on a specific Technologies "Types of location data used by Google" (undefined) Scenario Outline: Selecting different sublinks on the Google Terms footer link Then I verified the selected "Different types of location information may be used" Technology detail (undefined) Given I launch the Google home page url (undefined) And I scroll down to the end of the page Bahar (undefined) Given I select the Terms link in the Google footer (undefined) When I click on a Technologies and Principles tab (passed : 0s) And I click on a specific Technologies "How Google Wallet uses credit card numbers" (undefined) Scenario Outline: Selecting different sublinks on the Google Terms footer link Then I verified the selected "Google uses the credit card and debit card numbers" Technology detail (undefined) Given I launch the Google home page url (undefined) And I scroll down to the end of the page Bahar (undefined) Given I select the Terms link in the Google footer (undefined) |
80 | runner_cukes.RunCukesTest@1e88b3c |


Attributes used to describe a cell tower. The following optional fields are
not currently used, but may be included if values are available:
age
, signalStrength
, timingAdvance
.
| Field | Required | Type | Description |
|---|---|---|---|
cellId |
required | integer |
Unique identifier of the cell. On GSM, this is the Cell ID (CID); CDMA networks use the Base Station ID (BID). WCDMA networks use the UTRAN/GERAN Cell Identity (UC-Id), which is a 32-bit value concatenating the Radio Network Controller (RNC) and Cell ID. Specifying only the 16-bit Cell ID value in WCDMA networks may return inaccurate results. |
locationAreaCode |
required | integer |
The Location Area Code (LAC) for GSM and WCDMA networks. The Network ID (NID) for CDMA networks. |
mobileCountryCode |
required | integer |
The cell tower's Mobile Country Code (MCC). |
mobileNetworkCode |
required | integer |
The cell tower's Mobile Network Code. This is the MNC for GSM and WCDMA; CDMA uses the System ID (SID). |
age |
optional | integer |
The number of milliseconds since this cell was primary. If age is 0, the cellId represents a current measurement. |
signalStrength |
optional | number |
Radio signal strength measured in dBm. |
timingAdvance |
optional | number |
The timing advance value. |
{ "cellTowers": [ { "cellId": 170402199, "locationAreaCode": 35632, "mobileCountryCode": 310, "mobileNetworkCode": 410, "age": 0, "signalStrength": -60, "timingAdvance": 15 } ] }
Generated from the OpenAPI specification. Edit Report bug


Install the sweet.js compiler via npm:
npm install -g sweet.js
This installs the sjs
binary which is used to compile sweet.js code:
sjs --output out.js my_sweet_code.js
You can think of macros as functions that work on syntax. Much like a normal function you write a macro definition and then later invoke the macro with a syntax argument to produce new syntax. Running sweet.js code through the compiler will expand all macros and produce pure JavaScript that can be run in any JS environment.
Sweet.js provides two ways to define a macro: the simpler pattern-based rule macros and the more powerful procedural case macros (if you are familiar with Scheme or Racket these correspond to syntax-rules
and syntax-case
).
Rule macros work by matching a syntax pattern and generating new syntax based on a template.
To define a macro sweet.js provides a new macro
keyword that looks something like this:
macro <name> {
rule { <pattern> } => { <template> }
}
For example, here's a really simple identity macro that just expands to its syntax argument:
macro id {
rule {
// after the macro name, match:
// (1) a open paren
// (2) a single token and bind it to `$x`
// (3) a close paren
($x)
} => {
// just return the token that is bound to `$x`
$x
}
}
We can then invoke id
with:
id (42)
// expands to:
// 42
A pattern that begins with $
matches any token and binds it to that name in the template while everything else matches literally.
Note that a single token includes matched delimiters not just numbers and identifiers. For example, an array with all of its elements counts as one token:
id ([1, 2, 3])
// expands to:
// [1, 2, 3]
Macros can have multiple rules.
macro m {
rule { ($x) } => { $x }
rule { ($x, $y) } => { [$x, $y] }
}
m (1);
m (1, 2);
Rules are matched in-order and the first one to match the pattern will be used.
And macros can be recursively defined:
macro m {
rule { ($base) } => { [$base] }
rule { ($head $tail ...) } => { [$head, m ($tail ...)] }
}
m (1 2 3 4 5) // --> [1, [2, [3, [4, [5]]]]]
Repeated tokens can be matched with ellipses ...
.
macro m {
rule { ($x ...) } => {
// ...
}
}
m (1 2 3 4)
A repeated pattern with a separator between each item can be matched by adding (,)
between ...
and the pattern being repeated.
macro m {
rule { ($x (,) ...) } => {
[$x (,) ...]
}
}
m (1, 2, 3, 4)
Repeated groups of patterns can be matched using $()
.
macro m {
rule { ( $($id = $val) (,) ...) } => {
$(var $id = $val;) ...
}
}
m (x = 10, y = 2)
The syntax $[]
will match what is inside the brackets literally. For example, if you need to match ...
in a pattern (rather than have ...
mean repetition) you can escape it with $[...]
.
You can name pattern groups and literal groups. Sub-bindings can be referenced by concatenating the group name and binding name. All the syntax matched by the group will be bound to the group name.
macro m {
rule { ($binding:($id = $val) (,) ...) } => {
$(var $binding;) ...
// Or with sub-bindings
$(var $binding$id = $binding$val;) ...
}
}
macro m {
rule { $ellipsis:[...] } => {
// ...
}
}
A pattern name can be restricted to a particular parse class by using $name:class
in which case rather than matching a token the pattern matches all the tokens matched by the class.
macro m {
rule { ($x:expr) } => {
$x
}
}
m (2 + 5 * 10)
// --> expands to
2 + 5 * 10
The pattern classes are:
:ident
-- matches an identifier (eg. foo
):lit
-- matches a literal (eg. 100
or "a string"
):expr
-- matches an expression (eg. foo("a string") + 100
)Note that the syntax of custom pattern classes is currently experimental and subject to change.
You can define your own custom pattern classes with macroclass
.
// define the cond_clause pattern class
macroclass cond_clause {
pattern {
rule { $check:expr => $body:expr }
}
}
macro cond {
rule { $first:cond_clause $rest:cond_clause ... } => {
// sub-pattern variables in the custom class are
// referenced by concatenation
if ($first$check) {
$first$body
} $(else if ($rest$check) {
$rest$body
}) ...
}
}
cond
x < 3 => console.log("less than 3")
x == 3 => console.log("3")
x > 3 => console.log("greater than 3")
// expands to:
// if (x < 3) {
// console.log('less than 3');
// } else if (x == 3) {
// console.log('3');
// } else if (x > 3) {
// console.log('greater than 3');
// }
You can also define multiple patterns and bind missing sub-pattern variables using with
. You may supply multiple with
declarations or separate bindings by commas.
macroclass alias_pair {
pattern {
rule { $from:ident as $to:ident }
}
pattern {
rule { $from:ident }
with $to = #{ $from };
}
}
macro import {
rule { { $import:alias_pair (,) ... } from $mod:lit ;... } => {
var __module = require($mod);
$(var $import$to = __module.$import$from;) ...
}
rule { $default:ident from $mod:lit ;... } => {
var $default = require($mod).default;
}
}
import { a, b as c, d } from 'foo'
// expands to:
// var __module = require('foo');
// var a = __module.a;
// var c = __module.b;
// var d = __module.d;
Patterns with only a rule
declaration may be collapsed into just a rule
declaration in lieu of a pattern
.
The most important property of sweet.js is hygiene. Hygiene prevents variables names inside of macros from clashing with variables in the surrounding code. It's what gives macros the power to actually be syntactic abstractions by hiding implementation details and allowing you to use a hygienic macro anywhere in your code.
Hygiene protects against two kinds of naming collisions: binding collisions and reference collisions.
The part of hygiene most people intuitively grok is keeping track of the variable bindings that a macro introduces. For example, the swap macro creates a tmp
variable that should only be bound inside of the macro:
macro swap {
rule { ($a, $b) } => {
var tmp = $a;
$a = $b;
$b = tmp;
}
}
var tmp = 10;
var b = 20;
swap (tmp, b)
Without hygiene the two tmp
variables would clash:
var tmp = 10;
var b = 20;
var tmp = tmp;
tmp = b;
b = tmp;
Hygiene keeps the two tmp
bindings distinct by renaming them both:
var tmp$1 = 10;
var b = 20;
var tmp$2 = tmp$1;
tmp$1 = b;
b = tmp$2;
Hygiene also handles variable references. The body of a macro can contain references to bindings declared outside of the macro and those references must be consistent no matter the context in which the macro is invoked.
Some code to clarify. Let's say you have a macro that uses a random number function:
var random = function(seed) { /* ... */ }
let m = macro {
rule {()} => {
var n = random(42);
// ...
}
}
This macro needs to refer to random
in any context that it gets invoked. But its context could have a different binding to random
!
function foo() {
var random = 42;
m ()
}
Hygiene needs to keep the two random
bindings different. So sweet.js will expand this into something like:
var random$1 = function(seed) { /* ... */ }
function foo() {
var random$2 = 42;
var n = random$1(42);
// ...
}
Note that there is no way for hygiene to do this if it only renamed identifiers inside of macros since both random
bindings were declared outside of the macro. Hygiene is necessarily a whole program transformation.
By default sweet.js will rename every variable to something like name$102
. This gives correct but somewhat messy generated code. Usually this is not a problem since sweet.js provides source maps (with the --sourcemap
flag) so you rarely need to inspect the generated source. If you would like to have sweet.js generate cleaner code you can use the --readable-names
flag which will only rename variables when it is absolutely necessary. This flag is not yet turned on by default since it only supports ES5 code at the moment.
Sweet.js also provides a more powerful way to define macros: case macros. Case macros allow you to manipulate syntax using the full power of JavaScript. Case macros look like this:
macro <name> {
case { <pattern> } => { <body> }
}
Case macros look similar to rule macros with a few important differences. First, the patterns also match the macro name instead of just the syntax that comes after it:
macro m {
case { $name $x } => { ... }
}
m 42 // `$name` will be bound to the `m` token
// in the macro body
Most of the time you won't need to match the name and can use the wildcard _
pattern (which matches any token but doesn't bind it to a name) to ignore it:
macro m {
case { _ $x } => { ... }
}
The other difference from rule macros is that the body of a macro contains a mixture of templates and normal JavaScript that can create and manipulate syntax. For example, here's the identity macro defined with a case macro:
macro id {
case {_ $x } => {
return #{ $x }
}
}
Templates are now created with the #{...}
form (which is technically shorthand for syntax {...}
). The #{...}
form creates an array of syntax objects using any pattern bindings that are in scope (i.e. were matched by the pattern).
Syntax objects are the representation of tokens that sweet.js uses to keep track of lexical context (which is used to maintain hygiene). They can be created with templates (#{}
) but you can also create individual syntax objects using the lexical context of an existing syntax object:
macro m {
case {_ $x } => {
var y = makeValue(42, #{$x});
return [y]
}
}
m foo
// --> expands to
42
Sweet.js provides the following functions to create syntax objects:
makeValue(val, stx)
-- val
can be a boolean
, number
, string
, or null
/undefined
makeRegex(pattern, flags, stx)
-- pattern
is the string representation of the regex pattern and flags
is the string representation of the regex flagsmakeIdent(val, stx)
-- val
is a string representing an identifiermakePunc(val, stx)
-- val
is a string representing a punctuation (e.g. =
, ,
, >
, etc.)makeDelim(val, inner, stx)
-- val
represents which delimiter to make and can be either "()"
, "[]"
, or "{}"
and inner
is an array of syntax objects for all of the tokens inside the delimiter.If you want strip a syntax object of its lexical context and get directly at the token you can use unwrapSyntax(stx)
.
Case macros also provide throwSyntaxError(name, message, stx)
when you need to throw an error inside of a case macro. name
is a string that lets you name the error, message
is a string to describe what went wrong, and stx
is a syntax object or array of syntax objects that is used to print out the line number and surrounding tokens in the error message.
letstx
When using syntax object creation functions, it is convenient to refer to them in #{}
templates. To do this sweet.js provides letstx
, which binds syntax objects to pattern variables:
macro m {
case {_ $x } => {
var y = makeValue(42, #{$x});
letstx $y = [y], $z = [makeValue(2, #{$x})];
return #{$x + $y - $z}
}
}
m 1
// --> expands to
1 + 42 - 2
The left-hand side of the =
in a letstx
is actually a pattern so you can, for example, match a repeating pattern:
macro m {
case {_} => {
letstx $x ... = [makeValue(1, #{here}),
makeValue(2, #{here}),
makeValue(3, #{here})];
return #{
[$x (,) ...]
}
}
}
m
// expands to:
// [1, 2, 3]
Consider the following macro that matches against color options:
macro color_options {
rule { (red) } => { ["#FF0000"] }
rule { (green) } => { ["#00FF00"] }
rule { (blue) } => { ["#0000FF"] }
}
r = color_options (red)
g = color_options (green)
// expands to:
// r = ["#FF0000"]
// g = ["#00FF00"]
While this macro seems to work, attempting to generalize it quickly leads to a mess:
macro color_options {
rule { (red, red) } => {
["#FF0000", "#FF0000"]
}
rule { (red, green) } => {
["#FF0000", "#00FF00"]
}
rule { (red, blue) } => {
["#FF0000", "#0000FF"]
}
// ... etc.
}
r = colors_options (red, green)
g = colors_options (green, blue)
// expands to:
// r = ["#FF0000", "#00FF00"]
// g = ["#00FF00", "#0000FF"]
While it is possible to solve this problem through the use of case macros, the declarative intent is quickly lost in a mess of token manipulation code.
Sweet.js provides a solution to this problem with the :invoke
pattern class. This pattern class takes as a parameter a macro name which is inserted into the token tree stream before matching. If the inserted macro successfully matches its arguments, the result of its expansion is bound to the pattern variable. This makes declarative options simple to write:
macro color {
rule { red } => { "#FF0000" }
rule { green } => { "#00FF00" }
rule { blue } => { "#0000FF" }
}
macro colors_options {
rule { ($opt:invoke(color) (,) ...) } => {
[$opt (,) ...]
}
}
colors_options (red, green, blue, blue)
// expands to:
// ["#FF0000", "#00FF00", "#0000FF", "#0000FF"]
Invoke will only expand the parameterized macro once; if the macro expands to another macro it will not be expanded before being bound to the pattern variable.
If you need to keep expanding, use :invokeRec
which will continue to expand as long as a macro name is returned as the first token. This is useful if you have a recursive macro but is not the default because it can be unintuitive if the macro is not a recursive macro. For example,
macro a {
rule {} => { 1 + 2 }
}
macro b {
rule { $x } => { a + $x }
}
macro c {
rule { $y:invokeRec(b) } => { [$y] }
}
c 3
// expands to:
// [1 + 2] + 3
The b
macro returns a + $x
as its result. Since a
is another macro which returns 1 + 2
as a result, so 1 + 2
is what gets loaded into the $y
pattern variable, and the + 3
is surprisingly pushed outside the scope of the macro.
This is a change from the default behavior of version 0.5.0 in which :invoke
behaved like :invokeRec
and :invokeOnce
was available for the current default behavior.
Custom pattern classes act as an implicit invoke. This means that $opt:invoke(color)
is equivalent to $opt:color
:
macro color {
rule { red } => { "#FF0000" }
rule { green } => { "#00FF00" }
rule { blue } => { "#0000FF" }
}
macro colors_options {
rule { ($opt:color (,) ...) } => {
[$opt (,) ...]
}
}
colors_options (red, green, blue, blue)
// expands to:
// ["#FF0000", "#00FF00", "#0000FF", "#0000FF"]
Custom pattern classes only support single-token identifiers. If you need to call a multi-token name or a punctuator name, you should explicitly call $foo:invoke(...)
with the name.
Sometimes you want your custom operator to just return exactly what it matched rather than transform it into something else. Identity rules let you do just that. If you leave off the body of a rule, it will just return exactly the syntax that the rule matched:
macro func {
rule { function ($args (,) ...) { $body ... } }
rule { function $name:ident ($args (,) ...) { $body ... } }
}
macro checkFunc {
rule { $f:func } => { $f }
}
x = checkFunc function() {}
// expands to:
// x = function() {}
The function throwSyntaxCaseError
is similar to throwSyntaxError
, but should be used specifically when you are using :invoke
. Internally, sweet.js throws a SyntaxCaseError when a macro fails to match so throwSyntaxCaseError
just lets you do it manually. Here's how you might use it in a macro that checks for keyword:
macro keyword {
case { _ $kw } => {
var kw = #{ $kw };
if (kw[0].token.type === parser.Token.Keyword) {
return kw;
}
throwSyntaxCaseError('Not a keyword');
}
}
Sometimes you don't want a macro to recursively call itself. For example, say you want to override function
to add some logging information before the rest of the function executes:
macro function {
case {_ $name ($params ...) { $body ...} } => {
return #{
function $name ($params ...) {
console.log("Imma let you finish...");
$body ...
}
}
}
}
If you tried to run this through the compiler it will loop forever since the function
identifier in the macro expansion is bound to the function
macro. To prevent this you can use the let
macro binding form:
let function = macro {
case {_ $name ($params ...) { $body ...} } => {
return #{
function $name ($params ...) {
console.log("Imma let you finish...");
$body ...
}
}
}
}
This binds function
to the macro in the rest of the code but not in the body of the function
macro.
Sweet.js also lets you match on previous syntax using infix
rules. Use a vertical bar (|
) to separate your left-hand-side from your right-hand-side.
macro unless {
rule infix { return $value:expr | $guard:expr } => {
if (!($guard)) {
return $value;
}
}
}
function foo(x) {
return true unless x > 42;
return false;
}
You can use the infix
keyword with procedural macros, too. The macro name is just the first token on the right-hand-side.
macro m {
case infix { $lhs | $name $rhs } => { ... }
}
Infix rules can be mixed and matched with normal rules:
macro m {
rule infix { $lhs | $rhs } => { ... }
rule { $rhs } => { ... }
}
You can even leave off the right-hand-side for a postfix macro:
macro m {
rule infix { $lhs | } => { ... }
}
Sweet.js does its best to keep you from clobbering previous syntax.
macro m {
rule infix { ($args ...) | $call:expr } => {
$call($args ...)
}
}
(42) m foo; // This works
bar(42) m foo; // This is a match error
The second example fails to match because you'd be splitting the good function call on the left-hand-side in half. The result would have been nonsense, giving you a nasty parse error.
Custom operators let you define your own operators or override the builtin operators. They are similar to infix macros except rather than matching arbitrary syntax before and after the identifier name, both the left and right operands must be valid JavaScript expressions. You can think of them as infix macros with a pattern of { $left:expr | $right:expr }
. This limitation however means that you can define precedence and associativity for your custom operator.
There are two definition forms. One for binary operators and one for unary operators:
// binary operators
operator <name> <precedence> <associativity>
{ <left operand>, <right operand> } => #{ <template> }
// unary operators
operator <name> <precedence>
{ <operand> } => #{ <template> }
<name>
can be any valid macro name (ie. identifiers and punctuation but not delimiters, multi token names must be wrapped in parentheses)<precedence>
is a number (higher numbers bind more tightly)<associativity>
is either left
or right
<left operand>
/<right operand>
/<operand>
is a pattern variable (eg $left
) that will be bound in the template<template>
is what the operator will expand intoFor example, the following defines ^^
to be the power operator:
operator (^^) 14 right
{ $base, $exp } => #{ Math.pow($base, $exp) }
y + x ^^ 10 ^^ 100 - z
// expands to:
// y + Math.pow(x, Math.pow(10, 100)) - z;
The precedence of ^^
(14) is higher than the precedence of +
and -
(12, see the chart in the next section) so ^^
binds more tightly. Since we defined ^^
to be right associative, x ^^ 10 ^^ 100
is equivalent to x ^^ (10 ^^ 100)
. Left associative would have meant (x ^^ 10) ^^ 100
.
Custom operators and infix macros complement each other nicely.
macro (=>) {
rule infix { $param:ident | $body:expr } => {
function ($param) { return $body }
}
}
operator (|>) 1 left { $l, $r } => #{ $r($l) }
var res = 10 |> x => x * 2
|> y => y - 3
// expands to:
// var res = function (x) {
// return function (y) {
// return y - 3;
// }(x * 2);
// }(10);
Or let's say we want to chain promises:
macro (=>) {
rule infix { ($params ...) | { $body ... } } => {
function ($params ...) { $body ... }
}
}
operator (>>=) 12 left { $l, $r } => #{$l.then($r) }
getPromise('test.json') >>= JSON.parse >>= (response) => {
console.log("JSON Response!", response);
}
// expands to:
// getPromise('test.json').then(JSON.parse).then(function (response) {
// console.log('JSON Response!', response);
// });
Custom operators also let you change the behavior of the builtin operators:
operator + 12 left { $l, $r } => #{ add($l, $r) }
function add(x, y) {
// custom addition
}
100 + x - y * 5 + 30
// expands to
// function add(x, y) {
// // custom addition
// }
// add(add(100, x) - y * 5, 30);
You can even fix ==
!
operator == 9 left { $l, $r } => #{ $l === $r }
if ("42" == 42) {
// never runs!
}
// expands to:
// if ('42' === 42) {
// // never runs!
// }
Keep in mind that redefining the builtin operators needs to be done with care. While it's tempting to fix some of the wat implicit conversions of ==
/+
/-
and company, this could lead to hard to understand code. It should be used sparingly if at all. With great power...
The following charts note the precedence and associativity of the builtin operators. A higher precedence number means the operator binds more tightly.
| Operator | Precedence |
|---|---|
new |
16 |
++ |
15 |
-- |
15 |
! |
14 |
~ |
14 |
+ |
14 |
- |
14 |
typeof |
14 |
void |
14 |
delete |
14 |
yield |
2 |
| Operator | Precedence | Associativity |
|---|---|---|
* |
13 | left |
/ |
13 | left |
% |
13 | left |
+ |
12 | left |
- |
12 | left |
>> |
11 | left |
<< |
11 | left |
>>> |
11 | left |
< |
10 | left |
<= |
10 | left |
> |
10 | left |
>= |
10 | left |
in |
10 | left |
instanceof |
10 | left |
== |
9 | left |
!= |
9 | left |
=== |
9 | left |
!== |
9 | left |
& |
8 | left |
^ |
7 | left |
| |
6 | left |
&& |
5 | left |
|| |
4 | left |
The reader is what turns a string of code into tokens for the expander to work with. While it doesn't know anything about the semantics of JavaScript, it parses the code into tokens with several assumptions tailored to JavaScript. For example, it reads /abc/
as a single RegExp token, but it reads abc/d
as three distinct tokens: an identifier, a punctuator, and another identifier. In fact, the algorithm for determining when /
starts a regex or is a simple division without fully parsing the code is a key breakthrough that enables macros in JavaScript.
The default reader separates code into identifiers, literals, and punctuators. It also matches delimiters ({}()[]
) and creates a tree of tokens, so everything inside matching delimiters exist as children of a delimiter token.
You may want to extend the behavior of the reader, just like you would extend the semantics of a language with macros. Doing so allows you to embed custom syntax that may not even be valid according to the default reader, for example changing how the forward slash /
is handled. For the most part, you will use macros to extend JavaScript, but it might be useful to control exactly how the reader parses tokens.
sweet.js allows reader customization through readtables. A readtable is simple mapping from single characters to procedures that do the reading when the specified character is encountered in the source. For example, you can install a function to be called when <
is encountered, and you are responsible for reading as much of the source as you need and producing tokens.
Readtables allow composable extensions to the tokenizing phase, similar to how macros allow composable extensions to the parsing phase. You create a new readtable by extending an existing one. For example, here's how you extend the existing readtable with new functionality for <
:
var readtable = sweet.currentReadtable().extend({
'<': function(ch, reader) {
// read stuff here
}
});
And you can set the readtable programmatically:
sweet.setReadtable(readtable);
Or load it via a command line option. If you do this, the exported value of the module must be the readtable:
sjs --load-readtable ./readtable.js
# Or the shorthand
sjs -l ./readtable.js
By continually extending the current readtable, we can install the extensions in a modular way and build up a final readtable with our desired behaviors. You can pass multiple -l
(or --load-readtable
) flags to load multiple readtables extensions. If multiple readtables with the same character are installed, the last one loaded wins. There is currently no way to handle these kinds of collisions; every character must have a single unique handler (or none).
A readtable is an object that maps characters to handler functions, and an extend
method. Internally, a base readtable exists which is the current readtable by default. To create a new one, you always extend an existing one as explained above:
var readtable = sweet.currentReadtable().extend({
'<': function(ch, reader) {
reader.readPunctuator();
var token = reader.readToken();
if(token.type !== reader.Token.Identifier) {
return null;
}
// Continue parsing and return a single token or
// an array of tokens
}
});
Handler functions take two arguments: the character that invoked the handler and a reader object. You access the reader API through this reader object.
Currently, readtables are only invoked on punctuators. That means you can't customize what happens on a double quote "
or a delimiter like (
. This might be possible in the future.
The reader has not read the punctuator that invoked your handler, so you must always read past it with reader.readPunctuator()
first.
You can return null
from a handler function and the reader will automatically reset the state of the parser and continue reading normally. This lets you parse grammar that is ambiguous, and when something doesn't match you can simply bail. If you want to throw an error instead, you can use throwSyntaxError
in the reader API to show a nice error message to the user.
Lastly, you can return either a single token or an array of tokens from the handler. If you return multiple tokens, readToken
will only return the first one and queue up the rest.
There are two functions on the sweet module for working with readtables:
sweet.currentReadtable()
— Gets the current readtable, which always has an extend
method on it to install new extensions.
sweet.setReadtable(rt)
— Mutates the reader state to use the specified readtable when reading. Most likely users will use one or more -l
or --load-readtable
flags to sjs
, but you can set it progammatically with this method.
A reader object is always passed as the second argument when invoking a reader extension. It has several properties that are readable and writable:
reader.source
— The full raw string of the code being readreader.length
— The length of reader.source
reader.index
— The current position of the reader into the sourcereader.lineNumber
— The current line numberreader.lineStart
— The index into the source which the current line beganreader.extra
— read-only; an object that holds temporary data like commentsThe following functions are also available on the reader object. You can read tokens and skip comments with these. All of this functions (except skipComment
) with throw an error if it fails to read a valid token. You can use suppressReadError
to suppress this (explained more below).
reader.readIdentifier()
— Read an identifier (variable name, keyword, null and boolean literals)reader.readPunctuator()
— Read a punctuatorreader.readStringLiteral()
— Read a string literalreader.readNumericLiteral()
— Read a numeric literalreader.readRegExp()
— Read a regular expressionreader.readDelimiter()
— Read a delimiter, including everything inside it and up to the matching end delimiterreader.readToken()
— Read a token of any type. Note that this can trigger another readtable invocation if the current punctuator has an entry in the current readtable. If you use this, your reader extension could potentially be called recursively. In addition, you might need to be aware of how readtables queue tokens (see peekQueued
below).reader.skipComment()
— Skip past a comment and any whitespace, if it exits.The following functions are available for manually constructing tokens. You give it the parsed value and any options. The constructed tokens will automatically be given source location information, but it can't automatically infer the range. You should pass where the token theoretically started in the original source with the start
option. So a common pattern looks like this:
var start = reader.index;
// do some reading
return reader.makeIdentifier('foo', { start: start });
makeDelimiter
does not take any options because it can infer the range from the first and last inner tokens.
reader.makeIdentifier(value, opts)
— Make an identifier tokenreader.makePunctuator(value, opts)
— Make a punctuator tokenreader.makeStringLiteral(value, opts)
— Make a string literal token. It takes an addition boolean parameter octal
in opts
reader.makeNumericLiteral(value, opts)
— Make a numeric literal tokenreader.makeRegExp(value, opts)
— Make a regexp tokenreader.makeDelimiter(value, inner)
— Make a delimiter token with the token array inner
as its childrenThe following functions are simple utility functions:
reader.isIdentifierStart(charCode)
— Check if charCode
is a valid character for starting an identifierreader.isIdentifierPart(charCode)
— Check if charCode
is a valid non-starting character for an identifierreader.isLineTerminator(charCode)
— Check if charCode
is a character that terminates a linereader.peekQueued(offset)
— Inspects the tokens on the reader queue, returning the nth (based on offset
) token from the front (0 is the next token). Reader extensions are invoked from readToken
, but can return multiple tokens, so the rest are pushed onto a queue. You may need to be aware of this with recursive reader extensions or other complicated scenarios.reader.getQueued()
— Gets the next token from the reader queue and removes it from the queuereader.suppressReadError(readFunc)
— Suppresses exceptions from any of the read*
functions and returns null
instead if something went wrong. Example: reader.suppressReadError(reader.readIdentifier)
reader.throwSyntaxError(name, message, token)
— Throws a syntax error. name
is just a general identifier for your project, and message
is the specific message to display. You must pass a token
so it can properly locate where the error occurred in the original source.If you want to look at some examples, check out the readtables tests.
At the moment sweet.js supports a primitive form of module support with the --module
flag.
For example, if you have a file macros.js
that defines the m
macro:
// macros.js
macro m { /* ... */ }
export m;
and my_sweet_code.js
uses m
:
// my_sweet_code.js
m 42
You would compile this with:
$ sjs --module ./macros.js my_sweet_code.js
Note that modules must use the export
keyword. This allows modules to define "private" macros that are not visible to the main code.
The --module
flag uses the node path to look up the module file so you can publish and use macro modules on npm. Checkout lambda-chop for an example of this.
The biggest limitation with the current approach is that you can't arbitrarily interleave importing compile-time values (macros) and run-time values (functions). This will eventually be handled with support for "proper" modules (issue #43).
If you'd like to skip using the sjs
binary to compile your sweet.js code, you can use the node loader. This allows you to require
sweet.js files that have the .sjs
extension:
var sjs = require('sweet.js'),
example = require('./example.sjs');
example.one;
Where ./example.sjs contains:
// example.sjs
macro id {
rule { ($x) } => {
$x
}
}
exports.one = id (1);
Note that require('sweet.js')
must come before any requires of .sjs
code. Also note that this does not import any macros, it just uses sweet.js to compile files that contain macros before requiring them.
Alternatively, you can use sweet.loadMacro
to achieve a similar effect to the --module
command line flag:
var sweet = require('sweet.js');
// load all exported macros in `macros/str.sjs`
sweet.loadMacro('./macros/str');
// test.sjs uses macros that have been defined and exported in `macros/str.sjs`
require('./test.sjs');
This is basically equivalent to running sjs --module ./macros/str test.sjs
.
sweet.compile
Expands all macros and return the expanded code as a string.
(Str, {
sourceMap: Bool,
filename: Str,
readableNames: Bool,
maxExpands: Num
}) -> {
code: Str,
sourceMap: Str
}
sweet.compile(code, options)
Parameters:
code
the code to expandoptions
options object:
sourceMap
if true
generate a source mapfilename
file name of the source file to go into the source mapreadableNames
clean up variables that were renamed from hygiene (foo$100
becomes foo
where ever possible). Only supports ES5 code.maxExpands
maximum number of times to expand macros. Used to implement macro stepping.Return:
An object with two fields:
code
the expanded codesourceMap
the source mapsweet.parse
Expands all macros and returns an AST.
(Str, [...[...Syntax]], { maxExpands: Num }) -> AST
sweet.parse(code, modules, options)
Parameters:
code
the code to expandmodules
each array should be the result of expand
on a module. Any macros in the module that were export
ed will be in scope while expanding code
.options
options object:
maxExpands
maximum number of times to expand macros. Used to implement macro stepping.Return:
The abstract syntax tree. See the Parser API for details.
sweet.expand
Expands all macros and returns an array of syntax objects.
(Str, [...[...Syntax]], { maxExpands: Num }) -> [...Syntax]
sweet.expand(code, modules, options)
Parameters:
code
the code to expandmodules
each array should be the result of expand
on a module. Any macros in the module that were export
ed will be in scope while expanding code
.options
options object:
maxExpands
maximum number of times to expand macros. Used to implement macro stepping.Return:
An array of syntax objects. Syntax objects are an object with two fields:
token
which is a token object that esprima understands.context
holds hygiene information.Load sweet.js using AMD/require.js:
require(["./sweet"], function(sweet) {
// ...
});
Then just use the sweet.js compiler API. You can see an example of this in action with the sweet.js editor here and here.
Sometimes you really do need to break the wonderful protections provided by hygiene. Breaking hygiene is usually a bad idea but sweet.js won't judge.
Breaking hygiene is done by stealing the lexical context from syntax objects in the "right place". To clarify, consider aif
the anaphoric if macro that binds its condition to the identifier it
in the body.
var it = "foo";
long.obj.path = [1, 2, 3];
aif (long.obj.path) {
console.log(it);
}
// logs: [1, 2, 3]
This is a violation of hygiene because normally it
should be bound to the surrounding environment ("foo"
in the example above) but aif
wants to capture it
. To do this we can create an it
binding in the macro that has the lexical context associated with the surrounding environment. The lexical context we want is actually found on the aif
macro name itself. So we just need to create a new it
binding using the lexical context of aif
:
macro aif {
case {
// bind the macro name to `$aif_name`
$aif_name
($cond ...) {$body ...}
} => {
// make a new `it` identifier using the lexical context
// from `$aif_name`
var it = makeIdent("it", #{$aif_name});
letstx $it = [it];
return #{
// create an IIFE that binds `$cond` to `$it`
(function ($it) {
if ($cond ...) {
// all `it` identifiers in `$body` will now
// be bound to `$it`
$body ...
}
})($cond ...);
}
}
}
Comments in a rule macro or inside a #{...}
template should "just work". If you want to create comment strings programmatically you can use a token's leadingComments
property.
macro m {
case {_ () } => {
var x = makeValue(42, #{here});
x.token.leadingComments = [{
type: "Line",
value: "hello, world"
}];
return withSyntax ($x = [x]) #{
$x
}
}
}
m()
will expand to
//hello, world
42;
Often times you'll have an identifier or numeric literal token that you'd like to convert to a string literal. This little helper macro does just that:
macro to_str {
case { _ ($toks ...) } => {
return [makeValue(#{ $toks ... }.map(unwrapSyntax).join(''), #{ here })];
}
}
to_str(1 foo "bar")
// expands to:
// '1foobar'
You can use sjs --num-expands <number>
to walk through macro expansion one step at a time. This is particularly helpful when writing recursive macros.
The editor also supports stepping.
If you're trying to understand how a case macro is working two useful techniques are logging syntax objects to see what they actually contain and inserting debugger
statements to pause the debugger during expansion.
macro m {
case { _ $x } => {
console.log(#{$x})
debugger;
return #{42}
}
}
m 100


You need to enable JavaScript to run this app.
Student Mentorship System
Logout
Student Mentorship System
My Profile
Companies
Users
Report
Users
Logout
Users List
ADD
Active Users
Inactive Users
#
Name
Email
Role
Mobile No.
Address
City
Postal Code
State
Country
Edit
Deactivate
#
Name
Email
Role
Mobile No.
Address
City
Postal Code
State
Country
Edit
Activate
Add User
×
Name
Email
Password
Mobile No.
Organisation ID
Address
City
Postal Code
State
Country
Active
Role
--Select--
Student
Mentor
Sub Admin
Close
Submit
Edit User
×
Name
Email
Password
Mobile No.
Organisation ID
Address
City
Postal Code
State
Country
Role
--Select--
Student
Mentor
SubAdmin
Close
Update


Configuring Skupper sites using YAML
These instructions use a declarative YAML-based deployment.
See Getting Started for CLI based instructions for the same hello world
use case.
Overview
To show Skupper in action, we need an application to work with. This
guide uses an HTTP Hello World application with a frontend service and
a backend service. The frontend uses the backend to process requests.
In this scenario, the frontend is deployed in the west
namespace, and the backend is deployed in the east
namespace.
While these instructions use this particular application for demonstration purposes, the steps are the same for any Skupper deployment.
Prerequisites
You must have access to at least two Kubernetes namespaces. In the
steps below, replace west
and east
with your chosen namespaces.
Each namespace can reside on any cluster you choose, and you are not limited to two. You can have one on your laptop, another on Amazon, another on Google, and so on. For convenience, you can have them all on one cluster.
Skupper works with any flavor of Kubernetes. Here are some of your options for setting up Kubernetes clusters:
- Minikube
- Amazon Elastic Kubernetes Service
- Azure Kubernetes Service
- Google Kubernetes Engine
- IBM Kubernetes Service
- Red Hat OpenShift or OKD
- More providers
These instructions require kubectl
version 1.15 or later. See the
kubectl installation
guide for
more information.
Step 1: Configure access to multiple namespaces
Skupper is designed for use with multiple namespaces, typically on
different clusters. The skupper
command uses your kubeconfig and
current context to select the namespace where it operates.
To avoid getting your wires crossed, you must use a distinct kubeconfig or context for each namespace. The easiest way is to use separate console sessions.
Configure separate console sessions
Start a console session for each of your namespaces. Set the
KUBECONFIG
environment variable to a different path in each session.
export KUBECONFIG=$HOME/.kube/config-west
export KUBECONFIG=$HOME/.kube/config-east
Log in to your clusters
The methods for logging in vary by Kubernetes provider. Find the instructions for your chosen provider or providers and use them to authenticate and establish access for each console session.
$ <login-command-for-your-provider>
$ <login-command-for-your-provider>
See the following links for more information:
- Minikube
- Amazon Elastic Kubernetes Service
- Azure Kubernetes Service
- Google Kubernetes Engine
- Red Hat OpenShift or OKD
Set the current namespaces
Use kubectl create namespace
to create the namespaces you wish to
use. Use kubectl config set-context
to set the current namespace
for each session.
kubectl create namespace west
kubectl config set-context --current --namespace west
kubectl create namespace east
kubectl config set-context --current --namespace east
Step 2: Create a Skupper site in each namespace
Deploy the site controller
kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/0.3/cmd/site-controller/deploy-watch-current-ns.yaml
kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/0.3/cmd/site-controller/deploy-watch-current-ns.yaml
NOTE: To configure Skupper for all namespaces in a cluster, enter the following as a cluster administrator:
kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/master/cmd/site-controller/deploy-watch-all-ns.yaml
Create a Skupper site in the east namespace
Create a file named east-site.yml
:
apiVersion: v1
data:
cluster-local: "false"
console: "true"
console-authentication: internal
console-password: "rubble"
console-user: "barney"
edge: "true"
name: east-site
router-console: "true"
service-controller: "true"
service-sync: "true"
kind: ConfigMap
metadata:
name: skupper-site
Note that the data.name
value of east-site
and
setting data.edge
to true
in East disables network ingress at the
Skupper router layer. In our scenario, East needs to establish one
outbound connection to West. It does not need to accept any incoming
connections. As a result, no network ingress is required in East.
To apply the ConfigMap:
kubectl apply -f ~/east-site.yml
For more information about each parameter, see the Site Controller README.
After completion, you should see deployments named skupper-service-controller
and skupper-router
.
Create a Skupper site in the west Project
Create a file named west-site.yml
:
apiVersion: v1
data:
cluster-local: "false"
console: "true"
console-authentication: internal
console-password: "rubble"
console-user: "barney"
edge: "false"
name: west-site
router-console: "true"
service-controller: "true"
service-sync: "true"
kind: ConfigMap
metadata:
name: skupper-site
Note that the data.name
value of west-site
.
To apply the ConfigMap:
kubectl apply -f ~/west-site.yml
For more information about each parameter, see the Site Controller README.
After completion, you should see deployments named skupper-service-controller
and skupper-router
.
Check the installation
If you completed the Getting Started using the CLI you can check the status of each namespace, use the skupper status
command.
$ skupper status
Skupper is enabled for namespace 'west' in interior mode. It is not connected to any other sites. It has no exposed services.
$ skupper status
Skupper is enabled for namespace 'east' in edge mode. It is not connected to any other sites. It has no exposed services.
Step 3: Connect your namespaces
After installation, you have the infrastructure you need, but your namespaces are not connected. Creating a connection requires use of a connection-token.
In this step you generate a secret token that signifies permission to connect. The token also carries the connection details. When you connect Skupper sites, Skupper uses the connection token to establish a connection to the namespace that generated it.
Note: The connection token is truly a secret. Anyone who has the token can connect to your namespace. Make sure that only those you trust have access to it.
Generate a connection token
Requesting tokens requires the following format YAML file:
apiVersion: v1
kind: Secret
metadata:
labels:
skupper.io/type: connection-token-request
name: west-secret
Save as token-request.yaml
.
kubectl apply -f token-request.yaml
To verify this step and download the token:
kubectl get secret --export -o yaml west-secret > ~/west-secret.yaml
Use the token to form a connection
With the token in hand, you are ready to connect. Pass the token from West to East.
Apply the token:
kubectl apply -f ~/west-secret.yaml
Check the connection
If you completed the Getting Started using the CLI you can use the skupper status
command again to see if things have changed.
If the connection is made, you should see the following output:
$ skupper status
Skupper enabled for namespace 'west'. It is connected to 1 other site.
$ skupper status
Skupper enabled for namespace 'east'. It is connected to 1 other site.
Step 4: Expose your services
You now have a Skupper network capable of multi-cluster communication, but no services are attached to it. This step uses deployment annotations to make a Kubernetes deployment on one namespace available on all the connected namespaces.
In the examples below, we use the Hello World application to demonstrate service exposure. The same steps apply for your own application.
Deploy the frontend and backend services
Use kubectl create deployment
to start the frontend in West.
kubectl create deployment hello-world-frontend --image quay.io/skupper/hello-world-frontend
Likewise, use kubectl create deployment
to start the backend in
East.
kubectl create deployment hello-world-backend --image quay.io/skupper/hello-world-backend
Expose the backend service
At this point, we have the frontend and backend services running, but the frontend has no way to contact the backend. The frontend and backend are in different namespaces (and perhaps different clusters), and the backend has no public ingress.
Create annotations of the deployment in East to make
hello-world-backend
available in West.
kubectl annotate deployment/hello-world-backend skupper.io/proxy="http"
kubectl annotate deployment/hello-world-backend skupper.io/port="8080"
If you check the services in the OpenShift console of the west
project, you should now see hello-world-backend
.
Check the backend service
Use kubectl get services
in West to make sure the
hello-world-backend
service from East is represented. You should
see output like this (along with some other services):
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-world-backend ClusterIP <IP_ADDRESS> <none> 8080/TCP 1m30s
Test your application
To test our Hello World, we need external access to the frontend (not
the backend). Use kubectl expose
with --type LoadBalancer
to make
the frontend accessible using a conventional Kubernetes ingress.
kubectl expose deployment hello-world-frontend --port 8080 --type LoadBalancer
It takes a moment for the external IP to become available. If you are
using Minikube, you need to run minikube
tunnel
for this to work.
Now use curl
to see it in action. The embedded kubectl get
command below looks up the IP address for the frontend service and
generates a URL for use with curl
.
curl $(kubectl get service hello-world-frontend -o jsonpath='http://{.status.loadBalancer.ingress[0].ip}:8080/')
Note: If the embedded kubectl get
command fails to get the IP,
you can find it manually by running kubectl get services
and looking
up the external IP of the hello-world-frontend
service.
You should see output like this:
I am the frontend. The backend says 'Hello from hello-world-backend-869cd94f69-wh6zt (1)'.
Summary
Our simple HTTP application has two services. We deployed each service to a different Kubernetes cluster.
Ordinarily, a multi-cluster deployment of this sort means that the services have no way to communicate unless they are exposed to the public internet.
By introducing Skupper into each namespace, we were able to create a virtual application network that connects the services across cluster boundaries.
See the Hello World example for more detail.
The condensed version
export KUBECONFIG=~/.kube/config-west
<provider-login-command>
kubectl create namespace west
kubectl config set-context --current --namespace west
kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/0.3/cmd/site-controller/deploy-watch-current-ns.yaml
kubectl apply -f ~/west-site.yml
kubectl apply -f token-request.yaml
kubectl get secret --export -o yaml west-secret > ~/west-secret.yaml
kubectl create deployment hello-world-frontend --image quay.io/skupper/hello-world-frontend
kubectl expose deployment hello-world-frontend --port 8080 --type LoadBalancer
export KUBECONFIG=~/.kube/config-east
<provider-login-command>
kubectl create namespace east
kubectl config set-context --current --namespace east
kubectl apply -f https://raw.githubusercontent.com/skupperproject/skupper/0.3/cmd/site-controller/deploy-watch-current-ns.yaml
kubectl apply -f ~/east-site.yml
kubectl apply -f ~/west-secret.yaml
kubectl create deployment hello-world-backend --image quay.io/skupper/hello-world-backend
kubectl annotate deployment/hello-world-backend skupper.io/proxy="http"
kubectl annotate deployment/hello-world-backend skupper.io/port="8080"
curl $(kubectl get service hello-world-frontend -o jsonpath='http://{.status.loadBalancer.ingress[0].ip}:8080/')
Cleaning up
To remove Skupper and the other resources from this exercise, use the following commands:
kubectl delete configmap/skupper-site
kubectl delete service/hello-world-frontend
kubectl delete deployment/hello-world-frontend
kubectl delete configmap/skupper-site
kubectl delete deployment/hello-world-backend
Next steps
Now that you know how to connect services running on multiple clusters, here are a few more things to look at:


Report
Host:
crdc-b200-35
Metirc:
CPU
Memory
DF
Disk
Duration:
Last Hour
Last 12 Hours
Today
Interval:
1 Second
5 Second
15 Second
30 Second
1 Min


axiom¶
10 Axioms for L_{1}
10.1 Introduction
10.1.1 Axioms : easily recognizable valid sentences
10.1.2 (Modus Ponens) : A sentence Φ follows from sentences Ψ and χ by modus ponens iff χ = (Ψ -> Φ)
10.1.3 Definitionally equivalent to a formula Ψ ( a formula Φ ) : formulas χ, θ, Φ_{1}, Φ_{2} s.t. Φ and Ψ are alike except that one contains an occurrence of χ at some place where the other contains an occurrence of θ, and either
<IP_ADDRESS> Χ = (Φ_{1} v Φ_{2}) and θ = (-Φ_{1} -> Φ_{2})
<IP_ADDRESS> Χ = (Φ_{1} & Φ_{2}) and θ = -(Φ_{1} -> -Φ_{2})
<IP_ADDRESS> Χ = (Φ_{1} \(\iff\) Φ_{2}) and θ = ((Φ_{1}-> Φ_{2}) & (Φ_{2}-> Φ_{1}))
<IP_ADDRESS> Χ = (∃α)Φ_{1} and θ = -(α)-Φ_{1}
10.1.4 Proof : a finite sequence of sentences, each of which is an axiom or is definitionally equivalent to an earlier sentence of the sequence or follows from earlier sentences by modus ponens.
10.1.5 Theorem (a sentence Φ) : Φ is the last line of a proof.
<IP_ADDRESS> \(\vdash\) Φ : all closures of Φ are theorems.
10.1.6 Notation : Φ, Ψ, χ are formulas. α is variables. β γ are individual symbols. P, Q, R, T are expressions.
10.1.7 If Φ and Φ ->Ψ are theorems, then Ψ is a theorem.
10.1.8 If Φ is a sentence, Φ->Φ is a theorem.
10.1.9 If Φ, Ψ, χ are sentences, (Ψ -> χ)-> ((Φ->Ψ) ->(Φ->χ)) is a theorem.
10.1.10 If Φ->Ψ and Ψ->χ are theorems, then Φ->χ is a theorem.
10.1.11 (Mathematical induction) : All the positive integers have some given property P ?
<IP_ADDRESS> (weak induction)
<IP_ADDRESS>.1 1 has the property P
<IP_ADDRESS>.2 For every positive integer k, if k has the property P, then k+1 has the property P
<IP_ADDRESS> (Strong induction)
<IP_ADDRESS>.1 For each positive integer k, if all positive integers less than k have the property P, then k has the property P.
10.1.12 Q(Φ->Ψ) ->(QΦ->QΨ) is a theorem, if Q is a string of universal quantifiers containing every variable that occurs free in Φ or Ψ.
10.1.13 If QΦ and Q(Φ->Ψ) are theorems, then QΨ is a theorem, where Q is a string of universal quantifiers.
10.1.14 \(\vdash\)(Ψ->χ)->((Φ->Ψ)->(Φ->χ))
10.1.15 If Q(Φ->Ψ) and Q(Ψ->χ) are theorems, then Q(Φ->χ) is a theorem.
10.1.16 \(\vdash\)Q(Φ->Ψ) ->(QΦ->QΨ)
10.1.17 \(\vdash\)QΦ->Φ
10.1.18 \(\vdash\)Φ->QΦ, if no variable in Q occurs free in Φ.
10.1.19 \(\vdash\)QΦ->PΦ, if every variable in P is either in Q or does not occur free in Φ.
10.1.20 If Ψ and χ are closures of Φ, then Ψ->χ is a theorem, and χ is a theorem if Ψ is a theorem.
10.1.21 If \(\vdash\) Φ and \(\vdash\)Φ->Ψ, then \(\vdash\)Ψ.
10.1.22 If \(\vdash\)Φ and Ψ is definitionally equivalent to Φ, then \(\vdash\)Ψ.
10.1.23 If \(\vdash\)Φ->Ψ and \(\vdash\)Ψ->χ, then \(\vdash\)Φ->χ
10.1.24 \(\vdash\)Φ iff \(\vdash\)QΦ->QΨ
10.1.25 If \(\vdash\)Φ->Ψ, then \(\vdash\)QΦ->QΨ
10.2 Sentential calculus (lo\(\Vvdash\) bo\(\Vvdash\) ,full of one-liner theorems)
10.2.1 (Replacement) : If \(\vdash\)Φ\(\iff\)Ψ and χ is like θ except for having an occurrence of Φ at some place where θ has an occurrence of Ψ, then \(\vdash\)χ\(\iff\)θ and \(\vdash\)χ iff \(\vdash\)θ.
10.3 Quantification theory (lo\(\Vvdash\) bo\(\Vvdash\), full of one-liner theorems)
10.4 Identity, further quantification theory, and substitution
10.4.1 (Substitution) If Φ is a theorem and Ψ is a substitution-instance of Φ, then Ψ is a theorem.
<IP_ADDRESS> Stencil : an expression either a sentence of L_{1} or is obtainable from such a sentence by putting the counters ①, ②, ③, .. where that sentence contains occurrences of individual constants.
<IP_ADDRESS> Substitute σ for θ in Φ ( σ : stencil of degree n, θ : n-ary predicate other than I^{2}{1}, Φ : a sentence in L{1} ) : for each occurrence of θδ_{1}δ_{2}..δ_{n} in Φ where δ_{1},δ_{2},..,δ_{n} are individual symbols, put σ(δ_{1},δ_{2},..,δ_{n})
<IP_ADDRESS> Legitimate ( a substitution) : If in this substitution we have at all places obeyed the rule that no variable shall occur bound at any place in any substituted σ(δ_{1},δ_{2},..,δ_{n}) unless it already occurs bound at that place in σ
<IP_ADDRESS> A substitution-instance of a sentence Φ ( a sentence Ψ) : Ψ can be obtained from Φ by a legitimate substitution.
10.5 Proofs from assumptions
10.5.1 Proof of the sentence Φ from the assumptions Γ (a set of sentences Γ) : Φ is the last term of the sequence and each term of the sequence is either an axiom or one of the assumptions Γ or follows from earlier terms by modus ponens or definitional interchange.
10.5.2 Γ\(\Vvdash\)Φ : there is a proof of Φ from the assumptions Γ
10.5.3 (empty set) \(\Vvdash\) Φ iff Φ is a theorem
10.5.4 If Φ ∈ Γ, then Γ \(\Vvdash\) Φ
10.5.5 If Γ \(\Vvdash\) Φ and Γ ⊂ Δ, then Δ \(\Vvdash\) Φ
10.5.6 If Γ \(\Vvdash\) Φ and Δ \(\Vvdash\) Φ -> Ψ, then Γ U Δ \(\Vvdash\) Ψ
10.5.7 (Deduction Theorem) Γ U { Φ } \(\Vvdash\) Ψ iff Γ \(\Vvdash\) Φ -> Ψ
10.5.8 Γ \(\Vvdash\) Φ iff Γ U {-Φ} \(\Vvdash\) ‘P&-P’
10.5.9 Γ \(\Vvdash\) Φ iff for every sentence Ψ, Γ U { - Φ } \(\Vvdash\) Ψ
10.5.10 Γ \(\Vvdash\) Φ iff Φ is a consequence of Γ.


Overview
The Jetson Nano can be used in conjunction with or instead of a Raspberry Pi 3 on the Turtlebot 3's.
The main limitation of using the Jetson Nano is that it's only compatible with ROS Melodic, whereas the
Raspberry Pi's use ROS Kinetic. At the time of writing this, almost all packages used by the turtlebot
have been ported to ROS Melodic except for GMapping and possibly a few others. This is not necessarily
a problem if you are running most of the turtlebot packages on a separate computer, since you can use
an external computer running Kinetic to interface with the turtlebot running Melodic. It should also be
noted that the Jetson Nano does not come with WiFi capabilities, so you will need to get a
NICNetwork Interface Card if you
will be using the Jetson Nano as the sole
SBCSingle Board Computer.
A compatible NIC can be found
here,
and a complete set of parts lists can be found under the Resources tab.
Alternatively, you can get the most compatibility by running both the Jetson Nano and a Raspberry Pi
on the Turtlebot. The problems with doing that are physical space on the turtlebot, powering the
Jetson Nano, and adding the Jetson Nano to the ROS network. Physical space can be solved by adding more
plates to the turtlebot (if you run out of plates, they can be 3D printed relatively well). Powering
can be difficult because the OpenCR normally provides 5v to the Raspberry Pi, which can be used directly
if the Jetson Nano is used instead. It should be noted that you must put a jumper on J48 to switch to powering
from the barrel jack instead of from the USB port. If you want to use both the Jetson Nano and Raspberry Pi then the
Jetson Nano will need a different power supply (because the one on the OpenCR is limited to 4A max).
The OpenCR does have a power pass-through from the battery, which will need to be regulated to 5v. A
buck converted can be used to do this, and a suitable one can be found
here. Ideally,
the Jetson Nano would be powered from both of its 5v pins on the GPIO header (3A max each) and you would
not need to worry about not having enough power for peripherals. That being said, you can get away with
powering the Jetson through the barrel jack connector at a maximum of 4A or even through only one 5v header
pin, although I would highly recommend using both header pins because the Jetson will still pull over 4A
through the 3A rated pin, which could cause damage over time.


In case you’re experiencing problems with using ParaViewWeb, here is a list of solutions to some frequently encountered issues. If this page doesn’t help you solve your problem, try doing a search on GitHub or our Mailing list.
Can’t find application
ParaViewWeb is not an application in itself, but a library or framework.
If you were expecting an application out of ParaViewWeb you were probably looking for either Visualizer, LightViz or ArticViewer.


Rainer's Pizza
How Hungry Are You?
Personal 10"
Small 12"
Medium 16"
Large 20"
Backpacker 22"
CRUST
Thick & Chewy
Thin & Crisp
Cheesy Inside
Cauliflower
SAUCE
Crushed Tomato
Creamy Garlic
Pesto
Olive Oil
TOPPINGS
Mozzarella
Gorgonzola
Pepperoni
Spinach
Broccoli
Artichoke
Prosciutto
Order My Pizza!
Your Pizza Order
Size :
Crust :
Sauce :
Toppings :
Price : $
Purchase


Movie TimeCostSome Dumb thing5:00pm$5.00
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy
text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make
a type specimen book. It has survived not only five centuries, but also the leap into electronic
typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of
Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software
like Aldus PageMaker including versions of Lorem Ipsum.
waterfalls10:00pm$4.00
Waterfalls are commonly formed in the upper course of the river.[1] At these times the channel is often narrow and deep. When the river courses over resistant bedrock, erosion happens slowly, while downstream the erosion occurs more rapidly.[1][2] As the watercourse increases its velocity at the edge of the waterfall, it plucks material from the riverbed. Whirlpools created in the turbulence as well as sand and stones carried by the watercourse increase the erosion capacity.[1] This causes the waterfall to carve deeper into the bed and to recede upstream. Often over time, the waterfall will recede back to form a canyon or gorge downstream as it recedes upstream, and it will carve deeper into the ridge above it.[3] The rate of retreat for a waterfall can be as high as one and half meters per year.[1]
Some Dumb thing9:00pm$8.00
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy
text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type
specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining
essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem
Ipsum passages, and more recently awaterfall with desktop publishing software like Aldus PageMaker including versions
of Lorem Ipsum.


Dear Beloved in Christ, It is by the grace of God that I received Christ, knowing the truth and the truth have set me free. Having known the truth, I had no choice than to do what is lawful and right in the sight of God foreternal life and in the sight of man for witness of God�s mercy and glory upon my life.I have the pleasure to share my testimony with you, having seen your contact from the Internet. I am Barrister Phillip Nduka, the legal adviser to late Mrs.B.J. Hayford a British Woman that lived in my Country Nigeria for 25 years before she died in the Bellview plane crash October last year. These Lady was a good Christian, She so dedicated to God but she lost her husband so early and she had no child till she died. Throughout her stay in my country, she acquired a lot of properties like lands, house properties, etc. As her legal adviser, before her death, she instructed me to write her WILL. Because she had no child, she dedicated her wealth to God. According to the WILL, the properties have to be sold and the money be given out to a ministry for the work of God. As her legal adviser, all the documents for the properties were in my care. She gave me the authority to sell the properties and give out the fund to the Ministries for the work of God. In short, I sold all the properties after her death, as she instructed And as matter of fact, after I sold all her properties, I realized more than $6,000,000.00 (six million US dollars plus), and what supposed to be the percentage interest of my right legal fee was firstly deducted by me out of the total amount realized from the sold properties, this was base on the initial agreement between me and the owner of the properties before her death. Therefore the total amount left to be invested into God's work as instructed by the owner, is $6,000,000.00 (six million US dollars) only. But Instead of giving the main fund out for the work of God as instructed to me by the owner before her death. I converted the fund to myself with the intention of investing the fund abroad for my personal use. I was afraid of putting the fund in the Bank, because I have to give account to the bank on how I got the money. I then packaged the fund in consignments and deposited the consignments with a security company. I did not want the management of the Security Company to know the content of the consignments; therefore I registered the content of the consignments as Gold Bars. Now, the security company believes that what I deposited with them was Gold Bars. I had encounter with Christ when Rev. Billy Graham was preaching on television concerning Ananias and Saphira in Acts 5:1-11. After hearing the word of God, I gave my life to Christ and became a born again Christian.  But if the wicked turn from his wickedness, and do that which is lawful and right, he shall live thereby". From the scripture, I discovered that the only way I could have peace in my life is to do what is lawful and right by giving out the fund as instructed for the work of God by the owner before her death. I have asked God for forgiveness and I know that God have forgiven me. But I have to do what is lawful and right in the sight of God by giving out the fund to the chosen ministry for the purpose of God's work as instructed by the owner before her death. After my fasting and prayers, I asked God to make his choice and direct me to a honest Christian but after my fervent prayer over it, then you were nominated to me through divine revelation from God, so these are how I received such a divine revelation from the Lord, how I got your contact information, and I then decided to contact you for the fund to be used wisely for things that will glorify the name of God. I have notified the Security Company where I deposited the consignments that contained the fund, that I am moving the consignment abroad and the security company has since been waiting for my authority for the consignment to leave my country and move to abroad. So if you know that you will use this fund honestly and wisely for things that will glorify God's name,then do contact me back first through this my email<EMAIL_ADDRESS>for more details. You should also forward to me your physical contact address were you will like the consignments delivered to you, your direct telephone and fax number for easy communication and to fax you the documents concerning the consignments. Your prompt response will be highly appreciated. Yours in Christ. Barrister Phillip Nduka.


Set the desired position, size, and state of new browser windows
Left
pixels
Width
pixels
Top
pixels
Height
pixels
State
Normal
Maximized
Minimized
Full screen


$C33C/49980: Search for drive number Jump from $C32A: C33C: AA TAX ; note position C33D: A0 00 LDY #$00 C33F: A9 3A LDA #$3A ; ':' C341: DD 01 02 CMP $0201,X ; colon behind it? C344: F0 0C BEQ $C352 ; yes C346: DD 00 02 CMP $0200,X ; colon here? C349: D0 16 BNE $C361 ; no C34B: E8 INX Jump from $C35F: C34C: 98 TYA Jump from $C359, $C35D: C34D: 29 01 AND #$01 ; drive number Jump from $C366: C34F: A8 TAY C350: 8A TXA C351: 60 RTS Jump from $C344: C352: BD 00 02 LDA $0200,X ; get drive number C355: E8 INX C356: E8 INX C357: C9 30 CMP #$30 ; '0'? C359: F0 F2 BEQ $C34D ; yes C35B: C9 31 CMP #$31 ; '1'? C35D: F0 EE BEQ $C34D ; yes C35F: D0 EB BNE $C34C ; no, use last drive number Jump from $C349: C361: 98 TYA ; last drive number C362: 09 80 ORA #$80 ; set bit 7, uncertain drive number C364: 29 81 AND #$81 ; erase remaining bits C366: D0 E7 BNE $C34F Previous Page / Next Page


Investment Options
Investment options aren’t just for those planning for retirement, or for those with a high net worth. We offer a suite of financial strategies to help you navigate every life goal at every life stage. Our bank offers a wide range of retirement and investment strategies that you can use to help plan for a financially secure retirement.
Mutual Funds
Mutual funds are a cornerstone of today’s active portfolio. Our Financial Consultants will help you choose investment options that are in line with your risk tolerance, time horizon, and investment goals.
Asset Allocation
A smart asset allocation strategy1a balances risk and reward, helping you potentially achieve better returns while minimizing overall investment risk.
Managed Strategies
Managed strategies are now available to most investors. Santander Investment Services provides options for competitively priced managed accounts with an initial minimum investment requirement of $25,000.
Life Insurance
While you can't predict the future, personal insurance strategies from Santander Investment Services can help you plan for the unknown


DEPRECATED. ThemeService has been deprecated since 0.35.0 version in favor of
ThemingModule and ThemingService which provides more flexibility and
functionality to the developers.
Theme switcher API allows user to set specific theme programmatically. There is no longer need for each
application to implement its own theme switcher code.
At the moment this won't work for Internet Explorer 11. We will enhance this feature in the near future.
The API requires additional assets from the npm packages: @sap-theming/theming-base-content
and
fundamental-styles
.
Prerequisites - add those objects to assets array in angular.json
:


Volunteer Data Today!
30for30 is committed to open data.
Before contributing, please note that the game replays you submit will be shared publicly, with appropriate protections.
If you've checked our privacy page and are comfortable with our terms, please proceed.
Thank you for contributing! To set up Data Drinker:
Latest Version: Data Drinker
To update Data Drinker:
Each contributor is randomly assigned a token. The tokens help us group games by player, but (probably) aren't much use to you.
In the future, we may do giveaways or otherwise show our appreciation to our contributors, and might use the tokens then.
If you want to view your user token,


Real Time Tune
Average Price: $39.99
Today's Price: $29.99
Diamond Bundle
Average Price: $399.99
Today's Price: $299.99