Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

executable file 2166 lines (1719 sloc) 86.474 kB
use File::Temp 'tempfile';
use Carp 'carp';
use Digest::SHA 'sha256_base64';
my %data;
my %transient;
my %externalized_functions;
my @data_types;
my @script_args;
sub meta::define_form {
my ($namespace, $delegate) = @_;
push @data_types, $namespace;
*{"meta::${namespace}::implementation"} = $delegate;
*{"meta::$namespace"} = sub {
my ($name, $value) = @_;
chomp $value;
$data{"${namespace}::$name"} = $value;
$delegate->($name, $value);
meta::define_form 'meta', sub {
my ($name, $value) = @_;
eval $value;
carp $@ if $@;
meta::meta('datatypes::bootstrap', <<'__guYWiOv4zBmdrlI3k3sW7f/q/xsX38Xvzz0dwwLCIRM');
meta::define_form 'bootstrap', sub {};
meta::meta('datatypes::code_filter', <<'__gIaVDqDoWT04Y/vzEYyoyadmT36MLhB1ERa09udQZ9M');
meta::define_form 'code_filter', sub {
my ($name, $value) = @_;
*{"code_filter::$name"} = eval "sub {\n$value\n}";
carp $@ if $@;
meta::meta('datatypes::data', <<'__j7lFraXGRfKk8ymj2mDJhNbCQMk9FSciN1hdDhzM99U');
meta::define_form 'data', sub {
my ($name, undef) = @_;
$externalized_functions{$name} = "data::$name";
*{$name} = sub {
associate("data::$name", $_[1] || join('', <STDIN>)) if @_ > 0 && $_[0] eq '=';
meta::meta('datatypes::function', <<'__XSIHGGHv0Sh0JBj9KIrP/OzuuB2epyvn9pgtZyWE6t0');
meta::define_form 'function', sub {
my ($name, $value) = @_;
$externalized_functions{$name} = "function::$name";
*{$name} = eval "sub {\n$value\n}";
carp $@ if $@;
meta::meta('datatypes::internal_function', <<'__heBxmlI7O84FgR+9+ULeiCTWJ4hqd079Z02rZnl9Ong');
meta::define_form 'internal_function', sub {
my ($name, $value) = @_;
*{$name} = eval "sub {\n$value\n}";
carp $@ if $@;
meta::meta('datatypes::library', <<'__Uu9nRiHABRn+x19zBWmHpJF8gbfMA5v5MlpNoSE8MqE');
meta::define_form 'library', sub {
my ($name, $value) = @_;
eval $value;
$externalized_functions{$name} = "library::$name";
*{$name} = sub {edit("library::$name")};
warn $@ if $@;
meta::meta('datatypes::line_filter', <<'__/k1BhEweDsgaEdqrALbIEjvKhsVrk/hv//e/KPydA6A');
meta::define_form 'line_filter', sub {
my ($name, $value) = @_;
*{"line_filter::$name"} = eval "sub {\n$value\n}";
carp $@ if $@;
meta::meta('datatypes::list-type', <<'__OKczvJ+6wi8VPNFcZ9ohlXjw+ychodWCfcELdli9p+w');
meta::define_form '_list_type', sub {
my ($outer_name, $outer_value) = @_;
$externalized_functions{$outer_name} = "_list_type::$outer_name";
*{$outer_name} = sub {
associate("${outer_value}::$_", '') for @_;
meta::define_form $outer_value, sub {
my ($name, $value) = @_;
$externalized_functions{$name} = "${outer_value}::$name";
*{$name} = sub {
my ($command, @xs) = @_;
my $xs = join "\n", @xs;
return grep length, split /\n/, retrieve("${outer_value}::$name") if $command eq 'items';
associate("${outer_value}::$name", retrieve("${outer_value}::$name") . "\n$xs") if $command eq 'add' || $command eq '<<';
edit("${outer_value}::$name") if $command eq 'edit';
return retrieve("${outer_value}::$name");
meta::meta('datatypes::note', <<'__TGOjJwmj+QJp1giUQqg2bEaQe8RvqnrFEqyZhIpSC34');
meta::define_form 'note', sub {
my ($name, undef) = @_;
$externalized_functions{$name} = "note::$name";
*{$name} = sub {edit("note::$name")};
meta::meta('datatypes::unlit_converter', <<'__2X1thh9LQ0oUWCfeWj297YwiGJA/klB7hVI2sWEFtEE');
meta::define_form 'unlit_converter', sub {
my ($name, $value) = @_;
*{"unlit_converter::$name"} = eval "sub {\n$value\n}";
carp $@ if $@;
meta::meta('datatypes::vim-highlighter', <<'__vsGBLVDC3S+pX/k/zl5CgXeAQz2QjpBkLgx0CJ4vcn0');
meta::define_form 'vim_highlighter', \&meta::bootstrap::implementation;
meta::meta('internal::runtime', <<'__Nd6Dp1A6nL7yAGeoRfeZETeaW8vnPN8HI9Diqo66vDA');
meta::define_form 'internal', \&meta::meta::implementation;
meta::_list_type('list', <<'__ozA5XMClOtEgdzZUav/0c1lAk3Vku/dc4e2tQHgNkTk');
meta::bootstrap('initialization', <<'__plktoDCjGQioE48vwfrH0xL3ulcYnTWp+fUvaFwRnnc');
use File::Temp 'tempfile';
use Carp 'carp';
use Digest::SHA 'sha256_base64';
my %data;
my %transient;
my %externalized_functions;
my @data_types;
my @script_args;
sub meta::define_form {
my ($namespace, $delegate) = @_;
push @data_types, $namespace;
*{"meta::${namespace}::implementation"} = $delegate;
*{"meta::$namespace"} = sub {
my ($name, $value) = @_;
chomp $value;
$data{"${namespace}::$name"} = $value;
$delegate->($name, $value);
meta::define_form 'meta', sub {
my ($name, $value) = @_;
eval $value;
carp $@ if $@;
meta::bootstrap('pod', <<'__0h2CBA2cqa4qd6nox9dul6Jn9hcJFHw3uPdC89Xim7o');
=head1 NAME
object - Stateful file-based object
object [options] action [arguments...]
object shell
Stateful objects preserve their state between executions by rewriting themselves. Each time the script exits it replaces its contents with its new state. Thus
state management, for user-writable scripts, is completely transparent.
An object rewrites itself only if its state has changed. This may seem like a dangerous operation, but some checks are put into place to ensure that it goes
smoothly. First, the object is initially written to a separate file. Next, that file is executed and asked to provide a hashsum of its contents. The original
object is rewritten only if that hashsum is correct. This ensures that the replacement object is functional and has the right data.
Currently the only known way to lose your data is to edit the serialization-related functions in such a way that they no longer function. However, this is not
something most people will normally do. In the future there may be a locking mechanism to prevent unintentional edits of these attributes.
meta::code_filter('verbatim', <<'__AON9bxWzZOMpERGSJVrit8dkijYb5Re8IIg2PEC5i1s');
my ($line, %settings) = @_;
unless ($settings{'name'}) {
return '\begin{verbatim}' if $settings{'begin'};
return '\end{verbatim}' if $settings{'end'};
return $line;
meta::data('default-action', <<'__zmNcTqv/Xk9W26j7HjnKI1UwqitrGFM+7xrzhiAWxXc');
meta::data('document', <<'__YGCi/syAMnXXbny13zipjKpx0WLvRajMexm2ilDCLe4');
= Javascript in Ten Minutes
a Spencer Tipping
- Introduction
This guide is for anyone who knows some Javascript but would like a quick\footnote{Longer than ten minutes, despite what the title says.} intro to its advanced features. It will be easier
reading if you also know another functional language such as Ruby, Perl, Python, ML, Scheme, etc, since I don't really explain how first-class functions work.
- Types
Javascript has nine types. They are:
+ Null -- \verb|null|. Chucks a wobbly if you ask it for any attributes; e.g.~\verb|| fails. Never boxed.\footnote{Boxing is just a way of saying whether something has a pointer. A
boxed type is a reference type, and an unboxed type is a value type. In Javascript, this has additional ramifications as well -- see section \ref{sec:boxing}.}
+ Undefined -- \verb|undefined|. What you get if you ask an object for something it doesn't have; e.g.~\verb|document.nonexistent|. Also chucks a wobbly if you ask it for any attributes.
Never boxed.
+ Strings -- e.g. \verb|'foo'|, \verb|"foo"| (single vs. double quotation marks makes no difference). Sometimes boxed. Instance of \verb|String| when boxed.
+ Numbers -- e.g. \verb|5|, \verb|3e+10| (all numbers behave as floats -- significant for division, but can be truncated by \verb|x >>> 0|). Sometimes boxed. Instance of \verb|Number| when
+ Booleans -- \verb|true| and \verb|false|. Sometimes boxed. Instance of \verb|Boolean| when boxed.
+ Arrays -- e.g. \verb|[1, 2, "foo", [3, 4]]|. Always boxed. Instance of \verb|Array|.
+ Objects -- e.g. \verb|{foo: 'bar', bif: [1, 2]}|, which are really just hashtables. Always boxed. Instance of \verb|Object|.
+ Regular expressions -- e.g. \verb|/foo\s*([bar]+)/|. Always boxed. Instance of \verb|RegExp|.
+ Functions -- e.g. \verb|function (x) {return x + 1}|. Always boxed. Instance of \verb|Function|.
The value \verb|null| is actually almost never produced by Javascript. The only case you're likely to run across \verb|null| is if you assign it somewhere (most of the time you'll get
\verb|undefined| instead -- one notable exception is \verb|document.getElementById|, which returns \verb|null| if it can't find an element). Making sparing use of \verb|undefined| and
instead using \verb|null| can make bugs much easier to track down.
- Functions
Functions are first-class lexical closures,\footnote{First-class in the sense that you can pass them around as values at runtime. You can't reliably introspect them, however, because while
you can obtain their source code via {\tt toString} you won't be able to access the values they close over.} just like \verb|lambda|s in Ruby or \verb|sub|s in Perl.\footnote{Note that block
scoping isn't used -- the only scopes that get introduced are at function boundaries.} They behave pretty much like you'd expect, but there are several really cool things about functions and
one really egregious disaster.
- Variadic behavior (a cool thing)
Functions are always variadic.\footnote{The number of arguments a function accepts is referred to as its {\it arity}. So a unary function, which is monadic, takes one, a binary function,
which is dyadic, takes two, etc. A function that takes any number of arguments is said to be variadic.} Formal parameters are bound if they're present; otherwise they're \verb|undefined|.
For example:
(function (x, y) {return x + y}) ('foo') // => 'fooundefined'
The arguments to your function can be accessed in a first-class way, too:
var f = function () {return arguments[0] + arguments[1]};
var g = function () {return arguments.length};
f ('foo') // => 'fooundefined'
g (null, false, undefined) // => 3
{\it The {\tt arguments} keyword is not an array!} It just looks like one. In particular, doing any of these will cause problems:
arguments.concat ([1, 2, 3])
[1, 2, 3].concat (arguments)
arguments.push ('foo')
arguments.shift ()
To get an array from the \verb|arguments| object, you can say \verb| (arguments)|. As far as I know that's the best way to go about it.
- Lazy scoping (a cool thing)
Internally, functions use a lexical scoping chain. However, the variables inside a function body aren't resolved until the function is called. This has some really nice advantages,
perhaps foremost among them self-reference:
var f = function () {return f};
f () === f // => true
{\bf Tidbit of pathology:} An important consequence of lazy scoping is that you can create functions that refer to variables that might never exist. This makes Javascript very difficult
to debug. The good part is that Javascript can be made to support syntactic macros via the \verb|toString| method:
var f = function () {return $0 + $1};
var g = eval (f.toString ().replace (/\$(\d+)/g,
function (_, digits) {return 'arguments[' + digits + ']'}));
g (5, 6) // => 11 (except on IE)
Theoretically by extending this principle one could implement true structural macros, operator overloading, a type system,\footnote{God forbid.} or other things.
- The meaning of {\tt this} (the egregious disaster)
One would think it is a simple matter to figure out what \verb|this| is, but it's apparently quite challenging, and Javascript makes it look nearly impossible. Outside of functions (in
the global scope, that is), the word \verb|this| refers to the {\it global object}, which is \verb|window| in a browser. The real question is how it behaves inside a function, and that
is determined entirely by how the function is called. Here's how that works:
+ If the function is called alone, e.g.~\verb|foo(5)|, then inside that function's body the word \verb|this| will be equivalent to the global object.
+ If the function is called as a method, e.g.~\verb||, then inside that function's body the word \verb|this| refers to the object, in this case \verb|x|.
+ If the function starts off as a method and then is called alone:
var f =;
f (5);
then \verb|this| will be the global object again. Nothing is remembered about where \verb|f| came from; it is all determined right at the invocation site. \label{itm:forget}
+ If the function is invoked using \verb|apply| or \verb|call|, then \verb|this| points to whatever you set it to (unless you try to set it to \verb|null| or \verb|undefined|, in
which case it will be the global object again):
var f = function () {return this}; (4) // => 4 (0) // => 0 (false) // => false (null) // => [object global]
Given this unpredictability, most Javascript libraries provide a facility to set a function's \verb|this| binding (referred to within Javascript circles as just a function's binding)
to something invocation-invariant. The easiest way to do this is to define a function that proxies arguments using \verb|apply| and closes over the proper value (luckily, closure
variables behave normally):
var bind = function (f, this_value) {
return function () {return f.apply (this_value, arguments)};
The difference between \verb|call| and \verb|apply| is straightforward: \verb| (x, y, z)| is the same as \verb|f.apply (x, [y, z])|, which is the same as
\verb|bind (f, x) (y, z)|. That is, the first argument to both \verb|call| and \verb|apply| becomes \verb|this| inside the function, and the rest are passed through. In the case of
\verb|apply| the arguments are expected in an array-like thing (\verb|arguments| works here), and in the case of \verb|call| they're passed in as given.
- Important consequence: eta-reduction
In most functional programming languages, you can eta-reduce things; that is, if you have a function of the form \verb|function (x) {return f (x)}|, you can just use \verb|f| instead.
But in Javascript that's not always a safe transformation; consider this code:
Array.prototype.each = function (f) {
for (var i = 0, l = this.length; i < l; ++i)
f (this[i]);
var xs = [];
some_array.each (function (x) {xs.push (x)});
It might be tempting to rewrite it more concisely as:
some_array.each (xs.push);
\noindent however this latter form will result in a mysterious Javascript error when the native \verb|Array.push| function finds \verb|this| to be the global object instead of \verb|xs|.
The reason should be apparent: when the function is called inside \verb|each|, it is invoked as a function instead of a method. The fact that the function started out as a method on
\verb|xs| is forgotten. (Just like case \ref{itm:forget} above.)
The simplest way around this is to bind \verb|xs.push| to \verb|xs|:
some_array.each (bind (xs.push, xs));
- Odd tidbit: {\tt this} is never falsy
For reasons explained in section \ref{sec:boxing}, {\tt this} will never be set to a falsy value. If you try to set it to {\tt null} or {\tt undefined}, say by doing this:
var f = function () {
return this;
}; (null); // returns null, right?
\noindent it will in fact become the global {\tt this}, usually {\tt window} in the browser. If you use a falsy primitive, {\tt this} will refer to a boxed version of that primitive.
This has some counterintuitive consequences, covered in more detail in section \ref{sec:autoboxing}.
- Gotchas
Javascript is an awesome language just like Perl is an awesome language and Linux is an awesome operating system. If you know how to use it properly, it will solve all of your problems
trivially (well, almost), and if you miss one of its subtleties you'll spend hours hunting down bugs. I've collected the things I've run into here, which should cover most of Javascript's
linguistic pathology.\footnote{There is plenty of this pathology despite Javascript being generally an excellent language. This makes it ideal both for people who want to get things done,
and for bug-connoisseurs such as myself.}
- Semicolon inference
You won't run into any trouble if you always end lines with semicolons. However, most browsers consider it to be optional, and there is one potential surprise lurking if you choose to omit
Most of the time Javascript does what you mean. The only case where it might not is when you start a line with an open-paren, like this:
var x = f
(y = x) (5)
Javascript joins these two lines, forming:
var x = f (y = x) (5)
The only way around this that I know of is to put a semicolon on the end of the first line.
- Void functions
Every function returns a value. If you don't use a \verb|return| statement, then your function returns \verb|undefined|; otherwise it returns whatever you tell it to. This can be a common
source of errors for people used to Ruby or Lisp; for instance,
var x = (function (y) {y + 1}) (5);
\noindent results in \verb|x| being \verb|undefined|. If you're likely to make this slip, there's an Emacs mode called ``js2-mode'' that identifies functions with no side-effects or return
values, and it will catch most of these errors.\footnote{Of course, that's if you're an Emacs person. If you prefer a {\it real} editor (wink), I wrote a custom JS highlighter that handles
some cases better than the builtin one: \url{}.}
- {\tt var}
Be careful how you define a variable. If you leave off the {\tt var} keyword, your variable will be defined in the global scope, which can cause some very subtle bugs:
var f = function () { // f is toplevel, so global
var x = 5; // x is local to f
y = 6; // y is global
As far as I know, the same is true in both types of \verb|for| loop:
for (i = 0; i < 10; ++i) // i is global
for (var i = 0; i < 10; ++i) // i is local to the function
for (k in some_object) // k is global
for (var k in some_object) // k is local to the function
- Lazy scoping and mutability
This is a beautiful disaster. Check this out:
var x = [];
for (var i = 0; i < 3; ++i)
x[i] = function () { return i; };
x[0](); // What will these be?
What will our three functions return when they are eventually called? You might expect them to return 0, 1, and 2, respectively, since those were the values of \verb|i| when they were
created. However they will actually all return 3. This is because of Javascript's lazy scoping: Upon creation, each function receives only a variable name and a scope in which to search
for it; the value itself is not resolved until the time of invocation, at which point \verb|i| will equal 3.
The simplest way to fix this is to wrap our assignment in an anonymous function that is evaluated immediately, introducing another layer of scope. The following code works because within
the enclosing anonymous function, the value of \verb|new_i| never changes.
for (var i = 0; i < 3; ++i)
(function (new_i) {
x[new_i] = function () { return new_i; };
By the way, you might be tempted to do this:
for (var i = 0; i < 3; ++i) {
var j = i;
x[j] = function () { return j; };
This won't work for same reason that our original example failed: \verb|j| will be scoped to the nearest enclosing function (remember that Javascript's scoping is function level, not block
level!), so its value is changing just as frequently as \verb|i|'s.
- Equality
Because the \verb|==| is lame, these are all true in Javascript:
null == undefined
false == 0
false == ''
'' == 0
true == 1
true == '1'
'1' == 1
So, {\it never use the {\tt ==} operator unless you really want this behavior}. Instead, use \verb|===| (whose complement is \verb|!==|), which behaves sensibly. In particular, \verb|===|
requires both operands to not only be the same-ish, but also be of the same type. It does referential comparison for boxed values and structural comparison for unboxed values. If one side
is boxed and the other is unboxed, \verb|===| will always return false. Because string literals are unboxed, though, you can use it there: \verb|'foo' === 'fo' + 'o'|.
There is one case in particular where {\tt ==} is more useful than {\tt ===}. If you want to find out whether something has a property table (i.e.~isn't {\tt null} or {\tt undefined}), the
easiest way to go about it is {\tt (x == null)} rather than the more explicit \verb+(x === null || x === undefined)+. Apart from this I can't imagine using {\tt ==} very
often.\footnote{And, in fact, there are good security reasons not to do so; see section \ref{sec:numeric-coercion} for all the gory details.}
{\bf Tidbit of pathology:} It turns out that {\tt ==} isn't even stable under truthiness. If {\tt x = 0} and {\tt y = new Number(0)}, then {\tt x == y}, {\tt !!x} is {\tt false}, and
{\tt !!y} is {\tt true}. Section \ref{sec:boxing} talks more about why this kind of thing happens.
- Boxed vs.~unboxed
Boxed values are always truthy and can store properties. Unboxed values will silently fail to store them; for example:\footnote{There are other consequences of boxing; see sections
\ref{sec:careful-with-typeof} and \ref{sec:careful-with-instanceof} for some examples.}
var x = 5; = 'bar'; // => undefined; x is an unboxed number.
var x = new Number (5); = 'bar'; // => 'bar'; x is a pointer.
How does a sometimes-boxed value acquire a box? When you do one of these things:
+ Call its constructor directly, as we did above
+ Set a member of its prototype and refer to \verb|this| inside that method (see section \ref{sec:prototypes})
+ Pass it as the first argument to a function's {\tt call} or {\tt apply} method (see section \ref{sec:this-is-never-falsy})
All HTML objects, whether or not they're somehow native, will be boxed.
- Things that will silently fail or misbehave
Javascript is very lenient about what you can get away with. In particular, the following are all perfectly legal:
[1, 2, 3].foo // => undefined
[1, 2, 3][4] // => undefined
1 / 0 // => Infinity
0 * 'foo' // => NaN
This can be very useful. A couple of common idioms are things like these:
e.nodeType || (e = document.getElementById (e)); = || 5;
Also, the language will convert {\it anything} to a string or number if you use \verb|+|. All of these expressions are strings:
null + [1, 2] // => 'null1,2'
undefined + [1, 2] // => 'undefined1,2'
3 + {} // => '3[object Object]'
'' + true // => 'true'
And all of these are numbers:
undefined + undefined // => NaN
undefined + null // => NaN
null + null // => 0
{} + {} // => NaN
true + true // => 2
0 + true // => 1
And some of my favorites:
null * false + (true * false) + (true * true) // => 1
true << true << true // => 4
true / null // => Infinity
[] == [] // => false
[] == ![] // => true
- Numeric coercion
This one caught me off guard recently. Javascript's type coercions sometimes have inconsistent properties. For example:
{} // truthy
!!{} // coerce to boolean, truthy
+{} // coerce to number, NaN, which is falsy
[] // truthy
!![] // coerce to boolean, truthy
+[] // coerce to number, 0, which is falsy
[] == false // true (because [] is really zero, or something)
[] == 0 // true
[] == '' // true (because 0 == '')
[] == [] // false (different references, no coercion)
[1] == [1] // false (different references, no coercion)
[1] == +[1] // true (right-hand side is number, coercion)
You need to watch out for things like this when you're using certain operators with non-numeric things. For example, this function will not tell you whether an array contains any truthy
var has_truthy_stuff = function (xs) {
var result = 0;
for (var i = 0, l = xs.length; i < l; ++i)
result |= xs[i];
return !!result;
has_truthy_stuff([{}, {}, 0]) // returns false
The reason \verb|has_truthy_stuff| returns false is because when \verb|{}| is coerced to a number, it becomes {\tt NaN}, which is falsy in Javascript. Using \verb+|=+ with {\tt NaN} is
just like using it with {\tt 0}; nothing happens. So {\tt result} remains {\tt 0} for all values of the array, and the function fails.
By the way, you can change what numeric coercion does by (re)defining the {\tt valueOf} method:
+{valueOf: function () {return 42}} // -> 42
Object.prototype.valueOf = function () {
return 15;
Array.prototype.valueOf = function () {
return 91;
+{} // -> 15
+[] // -> 91
+[1] // -> 91
It's worth thinking about this a little bit because it has some interesting implications. First, {\tt valueOf()} may not halt. For example:
Object.prototype.valueOf = function () {
while (true);
{} == 5 // never returns; {} is coerced to a number
+{} // never returns
!{} // returns false; this bypasses valueOf()
Second, {\tt valueOf} is just a regular Javascript function, so it can create security holes. In particular, suppose you're using {\tt eval()} as a JSON parser (not a good idea, by the
way) and didn't check the input for well-formedness first. If someone sends you \verb|{valueOf: function () {while (true);}}|, then your app will hang the first time it coerces the object
to a number (and this coercion can be implicit, like the {\tt == 5} case above).
{\bf Tidbit of pathology:} The numeric value of an array depends on its contents:
+[0] // 0
+[1] // 1
+[2] // 2
+[[1]] // 1
+[[[[[[[1]]]]]]] // 1
+[1, 2] // NaN
+[true] // NaN
+['4'] // 4
+['0xff'] // 255
+[' 0xff'] // 255
-[] // 0
-[1] // -1
-[1, 2] // NaN
The built-in numeric coercion on arrays will fail with a stack overflow error if your array is deeply nested enough. For example:
for (var x = [], a = x, tmp, i = 0; i < 100000; ++i) {
a.push(tmp = []);
a = tmp;
a.push(42); // the value we want, 100000 levels deep
x == 5 // stack overflow in V8
Fortunately, at least in V8, numeric coercion is still well-defined when you have an array that contains itself; so this example isn't nearly as much fun as it could be:\footnote{Though
this could easily change if you redefine {\tt valueOf()}.}
var a = [];
+a // 0
- Things that will loudly fail
There is a point where Javascript will complain. If you call a non-function, ask for a property of \verb|null| or \verb|undefined|, or refer to a global variable that doesn't
exist,\footnote{To get around the error for this case, you can say {\tt typeof foo}, where {\tt foo} is the potentially nonexistent global. It will return {\tt 'undefined'} if
{\tt foo} hasn't been defined (or contains the value {\tt undefined}).} then Javascript will throw a \verb|TypeError| or \verb|ReferenceError|. By extension, referring to local variables
that don't exist causes a \verb|ReferenceError|, since Javascript thinks you're talking about a global variable.
- Throwing things
You can \verb|throw| a lot of different things, including unboxed values. This can have some advantages; in this code for instance:
try {
throw 3;
} catch (n) {
// n has no stack trace!
\noindent the \verb|throw|/\verb|catch| doesn't compute a stack trace, making exception processing quite a bit faster than usual. But for debugging, it's much better to throw a proper
try {
throw new Error(3);
} catch (e) {
// e has a stack trace, useful in Firebug among other things
- Be careful with {\tt typeof}
Because it behaves like this:
typeof function () {} // => 'function'
typeof [1, 2, 3] // => 'object'
typeof {} // => 'object'
typeof null // => 'object'
typeof typeof // hangs forever in Firefox
{\tt typeof} is a really lame way to detect the type of something in many cases.\footnote{And because it returns a string, it's marginally slower than using {\tt .constructor}.} Better is
to use an object's \verb|constructor| property, like this:
(function () {}).constructor // => Function
[1, 2, 3].constructor // => Array
({}).constructor // => Object
true.constructor // => Boolean
null.constructor // TypeError: null has no properties
In order to defend against \verb|null| and \verb|undefined| (neither of which let you ask for their constructor), you might try to rely on the falsity of these values:
x && x.constructor
But in fact that will fail for \verb|''|, \verb|0|, \verb|false|, \verb|NaN|, and possibly others. The only way I know to get around this is to just do the comparison:
x === null || x === undefined ? x : x.constructor
x == null ? x : x.constructor // same thing, but more concise
Alternatively, if you just want to find out whether something is of a given type, you can just use \verb|instanceof|, which never throws an exception.\footnote{Well, almost. If you ask for
it by putting {\tt null}, {\tt undefined}, or similarly inappropriate things on the right-hand side you'll get a {\tt TypeError}.}
- Also be careful with {\tt instanceof}
{\tt instanceof} is generally more useful than {\tt typeof}, but it only works with boxed values. For example, these are all false:
3 instanceof Number
'foo' instanceof String
true instanceof Boolean
\noindent However, these are all true:
[] instanceof Array
({}) instanceof Object
[] instanceof Object // Array inherits from Object
/foo/ instanceof RegExp // regular expressions are always boxed
(function () {}) instanceof Function
One way to work around the first problem is to wrap primitives:
new Number(3) instanceof Number // true
new String('foo') instanceof String // also true
new Boolean(true) instanceof Boolean // also true
In general, {\tt (new x.constructor(x) instanceof x.constructor)} will be true for all primitive {\tt x}. However, this doesn't hold for {\tt null} or {\tt undefined}. These will throw
errors if you ask for their constructors, and as far as I know are never returned from the result of a constructor invocation (using {\tt new}, that is).
- Browser incompatibilities
Generally browsers since IE6 have good compatibility for core language stuff. One notable exception, however, is an IE bug that affects \verb|String.split|:
var xs = 'foo bar bif'.split (/(\s+)/);
xs // on reasonable browsers: ['foo', ' ', 'bar', ' ', 'bif']
xs // on IE: ['foo', 'bar', 'bif']
A more subtle bug that took me several hours to find is that IE6 also doesn't return functions from \verb|eval()|:
var f = eval('function() {return 5}');
f() // on reasonable browsers: 5
f() // on IE6: 'Object expected' (because f is undefined)
I'm sure there are other similar bugs out there, though the most common ones to cause problems are generally in the DOM.\footnote{jQuery is your friend here. It's branded as a Javascript
library, but in fact it's a set of enhancements to the DOM to (1) achieve a uniform cross-browser API, and (2) make it easier to retrieve and manipulate nodes.}
- Prototypes
I used to have a very anti-OOP comment here, but considering that I occasionally use prototypes I removed it. Despite my obvious and probably unfair vendetta against Javascript's linguistic
compromises to pander to Java-inspired marketing pressure,\footnote{Hence its name, {\it Java}script, despite all of the dissimilarities.} prototype-based programming can be useful on
occasion. This section contains my subjective and biased view of it.
Whenever you define a function, it serves two purposes. It can be what every normal programmer assumes a function is -- that is, it can take values and return values, or it can be a
mutant instance-generating thing that does something completely different. Here's an example:
// A normal function:
var f = function (x) {return x + 1};
f (5) // => 6
This is what most people expect. Here's the mutant behavior that no rational person would ever imagine:
// A constructor function
var f = function (x) {this.x = x + 1}; // no return!
var i = new f (5); // i.x = 6
The following things are true at this point:
i.constructor === f
i.__proto__ === i.constructor.prototype // on Firefox, anyway
i instanceof f
typeof i === 'object'
The \verb|new| keyword is just a right-associative (prefix) unary operator, so you can instantiate things first-class:
var x = 5;
new x.constructor (); // Creates a boxed version of x, regardless of what x is
new new Function('x', 'this.x = 5');
If you are going to program using this questionable design pattern, then you'll probably want to add methods to things:\footnote{This section used to say that {\tt i.x} would evaluate to
{\tt 7}. That isn't true though. It's actually 6, as indicated. (Thanks to Daniel Gasparotto for pointing this out.)}
var f = function (x) {this.x = x};
f.prototype.add_one = function () {++this.x};
var i = new f (5);
i.add_one ();
i.x // => 6
You can find tons of information about this kind of prototype programming online.
- Why {\tt new} is awful
{\tt new} has some cool features (such as being first-class), but it has a really horrible shortcoming. Most functions in Javascript can be {\it forwarded} -- that is, you can write a new
function to wrap an existing one and the function being called will never know the difference. For example:
var to_be_wrapped = function (x) {return x + 1};
var wrapper = function () {
return to_be_wrapped.apply (this, arguments);
// for all x, wrapper(x) === to_be_wrapped(x)
However, {\tt new} has no such mechanism. You can't forward a constructor in the general case, because {\tt new} has no equivalent of {\tt apply}. (Though this isn't the whole story; see
the next section for a brilliant workaround.)
- Why {\tt new} isn't quite so awful
I recently received an e-mail from Ondrej Zara explaining that my bias against {\tt new} was ill-founded, and containing a remarkably elegant workaround for the problem I complained about
in the previous section. Here's his implementation verbatim:
var Forward = function(ctor /*, args... */) {
var tmp = function(){};
tmp.prototype = ctor.prototype;
var inst = new tmp();
var args = [];
for (var i=1;i<arguments.length;i++) { args.push(arguments[i]); }
ctor.apply(inst, args);
return inst;
\noindent And the use case:
var Class = function(a, b, c) {}
var instance = Forward(Class, a, b, c);
instance instanceof Class; // true
At first I was very skeptical that this approach would work, but I have yet to find a case where it fails. So constructors can indeed be forwarded in Javascript, despite my previous claims
to the contrary.
- Why you should use prototypes
If you need a dynamic-dispatch pattern, then prototypes are probably your best bet and you should use them rather than a roll-your-own approach. Google's V8 has a bunch of
prototype-specific optimizations, as do later releases of Firefox. Also, prototypes save memory; having a pointer to a prototype is much cheaper than having $n$ pointers to $n$ attributes.
If, on the other hand, you find yourself implementing actual inheritance hierarchies, then you're probably making a mistake.\footnote{OK, I'm being biased about this point. I tend to treat
Javascript more like Scheme than like Smalltalk, so I don't think much in terms of classical object-oriented modeling. Also, since closures are really fast, it's OK to use functional
abstraction instead of inheritance. Javascript tends to be better suited to metaprogramming than inheritance.} I have found prototypes to be an effective way to program in Javascript, but
inheritance in Javascript is (1) slow,\footnote{In some cases really slow. The difference between single-level and multiple-level prototype lookups in Firefox 3.5, for instance, is
enormous.} and (2) poorly representative of Javascript's ``everything is public'' model.
- Autoboxing
You might be tempted to try something like this:\footnote{{\tt !!~x} is just an idiom to make sure that {\tt x} ends up being a boolean. It's a double-negation, and {\tt !}~always
returns either {\tt true} or {\tt false}.}
Boolean.prototype.xor = function (rhs) {return !! this !== !! rhs};
And, upon running this code, you'd run into this tragically unfortunate property:
false.xor (false) // => true
The reason is that when you treat an unboxed value as an object (e.g.~invoke one of its methods), it gets temporarily promoted into a boxed value for the purposes of that method call.
This doesn't change its value later, but it does mean that it loses whatever falsity it once had. Depending on the type you're working with, you can convert it back to an unboxed value:
function (rhs) {return !! this.valueOf () !== !! rhs};
- A Really Awesome Equality
There is something really important about Javascript that isn't at all obvious from the way it's used. It is this: The syntax \verb|| is, in all situations, identical to
\verb|foo['bar']|. You could safely make this transformation to your code ahead of time, whether on value-properties, methods, or anything else. By extension, you can assign non-identifier
things to object properties:
var foo = [1, 2, 3];
foo['@snorkel!'] = 4;
foo['@snorkel!'] // => 4
You can also read properties this way, of course:
[1, 2, 3]['length'] // => 3
[1, 2, 3]['push'] // => [native function]
In fact, this is what the \verb|for (var ... in ...)| syntax was built to do: Enumerate the properties of an object. So, for example:
var properties = [];
for (var k in document) properties.push (k);
properties // => a boatload of strings
However, \verb|for ... in| has a dark side. It will do some very weird things when you start modifying prototypes. For example:
:: = 'bar';
var properties = [];
for (var k in {}) properties.push (k);
properties // => ['foo']
To get around this, you should do two things. First, never modify \verb|Object|'s prototype, since everything is an instance of \verb|Object| (including arrays and all other boxed things);
and second, use \verb|hasOwnProperty|:\footnote{OK, so you're probably wondering why we don't see the {\tt hasOwnProperty} method from a {\tt for ... in} loop, since it's obviously a
property. The reason is that Javascript's attributes have invisible flags (as defined by the ECMAScript standard), one of which is called {\tt DontEnum}. If {\tt DontEnum} is set for some
attribute, then a {\tt for ... in} loop will not enumerate it. Javascript doesn't provide a way to set the {\tt DontEnum} flag on anything you add to a prototype, so using {\tt
hasOwnProperty} is a good way to prevent looping over other people's prototype extensions. Note that it fails sometimes on IE6; I believe it always returns false if the prototype supplies an
attribute of the same name.}
:: = 'bar';
var properties = [], obj = {};
for (var k in obj) obj.hasOwnProperty (k) && properties.push (k);
properties // => []
And very importantly, never use \verb|for ... in| to iterate through arrays (it returns string indices, not numbers, which can cause problems) or strings. Either of these will fail if you
add methods to {\tt Array} or {\tt String} (or {\tt Object}, but you shouldn't do that).
- If You Have 20 Minutes...
Javascript can do almost anything that other languages can do. However, it might not be very obvious how to go about it.
- Iterators for cool people
Because languages like Ruby showed the world just how pass\'e \verb|for| loops really are, a lot of self-respecting functional programmers don't like to use them. If you're on Firefox, you
won't have to; the \verb|Array| prototype includes \verb|map| and \verb|forEach| functions already. But if you're writing cross-browser code and aren't using a library that provides them
for you, here is a good way to implement them:
Array.prototype.each = Array.prototype.forEach || function (f) {
for (var i = 0, l = this.length; i < l; ++i)
f (this[i]);
return this; // convenient for chaining
}; = || function (f) {
var ys = [];
for (var i = 0, l = this.length; i < l; ++i)
ys.push (f (this[i]));
return ys;
As far as I know this is (almost) the fastest way to write these functions. We declare two variables up-front (\verb|i| and \verb|l|) so that the length is cached; Javascript won't know
that \verb|this.length| is invariant with the \verb|for| loop, so it will check it every time if we fail to cache it. This is expensive because due to boxing we'd have a failed hash-lookup
on \verb|this| that then dropped down to \verb|this.__proto__|, where it would find the special property \verb|length|. Then, a method call would happen to retrieve
\verb|length|.\footnote{This gets into how Javascript presents certain APIs. Internally it has a notion of gettable and settable properties, though there isn't a cross-browser way to
create them. But properties such as {\tt length}, {\tt childNodes}, etc.~are all really method calls and not field lookups. (Try assigning to one and you'll see.)}
The only further optimization that could be made is to go through the array backwards (which only works for \verb|each|, since \verb|map| is assumed to preserve order):
Array.prototype.each = function (f) {
for (var i = this.length - 1; i >= 0; --i)
f (this[i]);
This ends up being very slightly faster than the first implementation because it changes a floating-point subtraction (required to evaluate \verb|<| for non-zero quantities) into a sign
check, which internally is a bitwise \verb|and| and a zero-predicated jump. Unless your Javascript engine inlines functions and you're really determined to have killer performance (at
which point I would ask why you're using Javascript in the first place), you probably never need to consider the relative overhead of a non-zero \verb|<| vs.~a zero \verb|>=|.
You can also define an iterator for objects, but not like this:
// NO NO NO!!! Don't do it this way!
Object.prototype.each = function (f) {
for (var k in this) this.hasOwnProperty (k) && f (k);
Much better is to implement a separate \verb|keys| function to avoid polluting the \verb|Object| prototype:
var keys = function (o) {
var xs = [];
for (var k in o) o.hasOwnProperty (k) && xs.push (k);
return xs;
- Java classes and interfaces
No sane person would ever want to use these. But if you're insane or are being forced to, then the Google Web Toolkit will give you a way to shoot yourself in the foot and turn it into
- Recursive metaclasses
There are different ways to approach this, but a straightforward way is to do something like this:\footnote{Remember that a class is just a function that produces instances. Nothing about
the {\tt new} keyword is necessary to write object-oriented code (thank goodness).}
var metaclass = {methods: {
add_to: function (o) {
var t = this;
keys (this.methods).each (function (k) {
o[k] = bind (t.methods[k], o); // can't use /this/ here
return o}}}; (metaclass, metaclass);
At this point, \verb|metaclass| is now itself a \verb|metaclass|. We can start to implement instances of it:
var regular_class = metaclass.add_to ({methods: {}});
regular_class.methods.def = function (name, value) {
this.methods[name] = value;
return this;
regular_class.methods.init = function (o) {
var instance = o || {methods: {}};
this.methods.init && (instance);
return this.add_to (instance);
regular_class.add_to (regular_class);
This is a Ruby-style class where you can define public methods and a constructor. So, for example:
var point = regular_class.init ();
point.def ('init', function () {this.x = this.y = 0});
point.def ('distance', function () {
return Math.sqrt (this.x * this.x + this.y * this.y)});
We're using the rather verbose \verb|this.x|, which may offend some Python-eschewing Rubyists. Fortunately, we can use dynamic rewriting to use the \verb|$| where Rubyists would use
\verb|@|:\footnote{And, in fact, we could bake this {\tt ruby()} transformation into a metaclass to make it totally transparent if we wanted to.}
var ruby = function (f) {
return eval (f.toString ().replace (/\$(\w+)/g,
function (_, name) {return 'this.' + name}));
point.def ('init', ruby (function () {$x = $y = 0}));
point.def ('distance', ruby (function () {
return Math.sqrt ($x * $x + $y * $y)}));
And now you can use that class:
var p = point.init ();
p.x = 3, p.y = 4;
p.distance () // => 5
The advantage of using metaclasses is that you can do fun stuff with their structure. For example, suppose that we want to insert method tracing into all of our points for debugging
purposes:\footnote{The example here used to contain the expression {\tt arguments.join}, which is invalid -- {\tt arguments} isn't an array. Now it uses the ``pretend this is an array for
the purposes of calling {\tt join} on it'' idiom, which usually works. (Though you'll sometimes get errors about methods not being generalized, as is the case on Chrome if you try to use
{\tt Array.prototype.toString()} this way.)}
keys (point.methods).each (function (k) {
var original = point.methods[k];
point.methods[k] = function () {
trace ('Calling method ' + k + ' with arguments ' + (arguments, ', '));
return original.apply (this, arguments);
Now \verb|trace| (which isn't a Javascript built-in, so you'd have to define it) would be called each time any method of a \verb|point| instance was called, and it would have access to
both the arguments and the state.
- Tail calls
Javascript does not do tail-call optimization by default, which is a shame because some browsers have short call stacks (the shortest I'm aware of is 500 frames, which goes by especially
quickly when you have bound functions and iterators). Luckily, encoding tail calls in Javascript is actually really simple:
Function.prototype.tail = function () {return [this, arguments]};
Function.prototype.call_with_tco = function () {
var c = [this, arguments];
var escape = arguments[arguments.length - 1];
while (c[0] !== escape)
c = c[0].apply (this, c[1]);
return escape.apply (this, c[1]);
We can now use this definition to write a tail-call optimized factorial function:\footnote{This technique is called {\em trampolining} and doesn't constitute implementing delimited
continuations, as I found out later. However, it's still pretty cool.}
// Standard recursive definition
var fact1 = function (n) {
return n > 0 ? n * fact1 (n - 1) : 1;
// Tail-recursive definition
var fact2 = function (n, acc) {
return n > 0 ? fact2 (n - 1, acc * n) : acc;
// With our tail-call mechanism
var fact3 = function (n, acc, k) {
return n > 0 ? fact3.tail (n - 1, acc * n, k) : k.tail (acc);
The first two functions can be called normally:
fact1 (5) // => 120
fact2 (5, 1) // => 120
\noindent though neither will run in constant stack space. The third one, on the other hand, will if we call it this way:
var id = function (x) {return x};
fact3.call_with_tco (5, 1, id) // => 120
The way this tail-call optimization strategy works is that instead of creating new stack frames:
5 * fact1(4)
4 * fact1(3)
\noindent or even creating hollow ones:
fact2(5, 1)
fact2(4, 5)
fact2(3, 20)
\noindent we pop out of the last stack frame before allocating a new one (treating the array of \verb|[function, args]| as a kind of continuation to be returned):
fact3(5, 1, k) -> [fact3, [4, 5, k]]
fact3(4, 5, k) -> [fact3, [3, 20, k]]
fact3(3, 20, k) ...
It isn't a bad performance hit, either -- the overhead of allocating a two-element array of pointers is minimal.
- Syntactic macros and operator overloading
Lazy scoping lets us do some cool stuff. Let's say we want to define a new syntax form for variable declaration, so that instead of this:
var f = function () {
var y = (function (x) {return x + 1}) (5);
\noindent we could write this:
var f = function () {
var y = (x + 1).where (x = 5);
This can be implemented in terms of regular expressions if we don't mind being woefully incorrect about half the time:
var expand_where = function (f) {
var s = f.toString ();
return eval (s.replace (/\(([^)]+)\)\.where\(([^)])\)/,
function (_, body, value) {
return '(function (' + value.split ('=')[0] + '){return ' +
body + '}) (' + value.split ('=', 2)[1] + ')';
Now we can say this:
var f = expand_where (function () {
var y = (x + 1).where (x = 5);
Obviously a proper parser is more appropriate because it wouldn't fail on simple paren boundaries. But the important thing is to realize that a function gives you a way to quote code, just
like in Lisp:
(defmacro foo (bar) ...)
(foo some-expression)
\noindent becomes this in Javascript (assuming the existence of \verb|parse| and \verb|deparse|, which are rather complicated):\footnote{Real versions of these are implemented in
\url{}, if you're interested to see what they look like. It's also a reasonable reference for syntactic edge cases.}
var defmacro = function (transform) {
return function (f) {
return eval (deparse (transform (parse (f.toString ()))));
var foo = defmacro (function (parse_tree) {
return ...;
foo (function () {some-expression});
This principle can be extended to allow for operator overloading if we write a transformation that rewrites operators into method calls:
x << y // becomes x['<<'](y)
Remember that property names aren't restricted to identifiers -- so we could overload the \verb|<<| operator for arrays to work like it does in Ruby with:
Array.prototype['<<'] = function () {
for (var i = 0, l = arguments.length; i < l; ++i)
this.push (arguments[i]);
return this;
The only thing that's unfortunate about implementing this stuff in Javascript rather than Lisp is that Javascript bakes syntactic constructs into the grammar, so trying to introduce new
syntactic forms such as \verb|when| isn't very convenient:
expand_when (function () {
when (foo) { // compile error; { unexpected
bar ();
But anything you can do inside the Javascript parse tree is fair game.\footnote{Keep in mind that {\tt toString} will sometimes rewrite your function to standard form, so leveraging
ambiguities of the syntax isn't helpful. In Firefox, for example, writing expressions with excess parentheses is not useful because those excess parentheses are lost when you call {\tt
- Further reading
I highly recommend reading jQuery (\url{}) for the quality and conscientiousness of the codebase. It's a brilliant piece of work and I've learned a tremendous amount by
pawing around through it.
Douglas Crockford has written some excellent Javascript references, including the well-known {\it Javascript: The Good Parts} and a less-well-known but free online tour of the language at
\url{}.\footnote{There are some discrepancies between his view of Javascript and mine. Neither is incorrect, there are just different unstated
assumptions. For example, when he says that there are three primitives he is correct; he counts types by the number of unboxed representations, whereas I count them by the number of literal
As a shameless plug, I also recommend reading through Divergence (\url{}), a library that I wrote. It's very different from jQuery -- much more
terse and algorithmic (and has no DOM involvement). jQuery uses a more traditional approach, whereas Divergence tends to make heavy use of closures and functional metaprogramming.
If you're into Lisp and metaprogramming, you might also enjoy \url{} and \url{}, two projects that
use function serialization and \verb|eval()| to implement some of the syntactic extensions mentioned in the last section.
Also, I recently found a site called \url{} that seems to be dedicated to exposing all of Javascript's edge-case pathologies. It's quite a fun and enlightening read. A more
in-depth look at the good, bad, and ugly parts of Javascript is \url{}; this site is written by one of the PrototypeJS developers and has convinced me that I really
don't know Javascript that well.
meta::data('header', <<'__bgoNmrNdosmFP/K+Dbg3jFkiPFpZJ3NadO5WCeO7tgY');
meta::data('intro', <<'__uirjwqAxCBH+vu23aaPBO6uNM7a7cUHAYKJo8gIJsGw');
This is a TeX document generator.
If this is your first time using it, then you'll probably want to install
the VIM highlighter for the custom TeX format. To do that, run this:
$ tex-document install-vim-highlighter
If you don't mind an extra line at the end of your .vimrc file, then you
can automatically associate .cltex files:
$ tex-document update-vimrc
Now you're ready to go.
To create a new TeX document, do this:
$ tex-document new newdoc
Next, edit the contents of your new document:
$ ./newdoc e
Once you're done editing, you can build and display the document:
$ ./newdoc make
Alternately, you can use the shell interface (exit with the 'exit' command or
$ ./newdoc shell
tex-document$ e
tex-document$ make
tex-document$ ^D
If you edit and run make a lot, I recommend you save your document just to be
on the safe side. Normally the document is not committed to disk until you exit
the shell, but you can commit at any time by using the 'save' command.
To extract your document in its original form, you can say this:
$ ./newdoc document > file
And to extract the generated TeX:
$ ./newdoc compile-to-tex > file
meta::data('meta-associations', <<'__Ql36C0s7UTZWfcWP/PM0xaUnVTY2wKIqUxjVsrvsZDU');
^function:: .pl
^internal_function:: .pl
^meta:: .pl
^bootstrap:: .pl
^data::document$ .cltex
^data::vim-highlighter$ .vim
^unlit_converter:: .pl
^line_filter:: .pl
^code_filter:: .pl
meta::data('name', <<'__ZkgTAmxwuI3x4+R+7A69S2WDL5ZYN9C8ZWldHH/i7xM');
meta::data('output-dir', <<'__6WcazSRIScVxZ8ZY+i+Wl1IEj3qxhKPc9cRstNVq4SQ');
meta::data('pdf-output-file', <<'__Fj+Eu1bcia5PjxWNF+/YZ0HliPDR62rUk90q9TJgMIk');
meta::data('pdf-reader', <<'__BlFO10Obn0hAHVxLShDpKtlpv0BTeJ7iqm1v7vjCM+A');
meta::data('pdftex', <<'__aHolEJEGN4wnHiydZyVzwRrETVuJhJOGo/nKL9tsLRY');
pdflatex -output-directory=__TEMPORARY_DIRECTORY__ __INPUT_FILE__
meta::data('table-of-contents', <<'__a4ayc/80/OGda4BO/1o/V0etpOqiLx1JwB5S3beHW0s');
meta::data('tex', <<'__36pVFkgFfbTr/DrquMMaLrTY5F5S/VJ5Z3Jc90KC+q4');
latex -output-directory=__TEMPORARY_DIRECTORY__ __INPUT_FILE__
meta::data('vim-highlighter', <<'__9M68+fUDGVDeeJP8SWHRqHPh2Bq1y6WM6Spq8bnK9yo');
" Cleaner TeX
" Maintainer: Spencer Tipping <>
" Language: Cleaner TeX (a variant of LaTeX)
if version < 600
syntax clear
elseif exists("b:current_syntax")
syn match cltTitle /^= .*$/
syn match cltAuthor /^a .*$/
syn match cltDate /^d .*$/
syn match cltBegin /^begin$/
syn match cltSection /^\s*- .*$/
syn region cltVerbatim start=/^\s*::$/ end=/^\s*:\.$/
syn match cltEnumeratedThing /^\s*[eid]\[/
syn match cltEnumeratedThing /^\s*\][eid]/
syn match cltItem /^\s*+\s/
syn match cltQuantifiedItem /^\s*+\[[^\]]*\]\s/
runtime! syntax/tex.vim
hi link cltBegin Keyword
hi link cltTitle Identifier
hi link cltAuthor Identifier
hi link cltDate Identifier
hi link cltEnumeratedThing Special
hi link cltItem Special
hi link cltQuantifiedItem Special
hi link cltSection Type
hi link cltVerbatim String
let b:current_syntax = "cltex"
meta::function('add-to', <<'__KBgra0vG1gIsUI8CCVf4ZEdCatZDCdVO6HuUx+jOJ9Q');
my ($filename) = @_;
my @members = grep /^implementation::/, keys %data;
for (@members) {
my $destination_name = basename($_);
open my($handle), "| $filename import $destination_name" or messages::error("Attribute $_ could not be written.");
print $handle retrieve($_);
close $handle;
meta::function('cat', <<'__h2PeSpk/pPmrzLRTTofdLTbhj06IWUw5WWke6ggUsdk');
my ($name) = @_;
meta::function('clean', <<'__YiaR22ZfeeFhhPYBUFHWBZqstuOzKtkv2XqmTuXhy1E');
my $output_directory = &{'pdf-output-file'}();
$output_directory =~ s+/.*++g;
unlink <$output_directory/*>;
rmdir $output_directory;
meta::function('clone', <<'__qP6xPZE75s9g0XJIiC6FGw0vnj2j0glUzsAHxyA3lvY');
for (@_) {
if ($_) {
eval {
file::write($_, serialize(), noclobber => 1);
chmod(0700, $_);
print "File $_ cloned successfully.\n";
print "$@\n" if $@;
meta::function('compile', <<'__HfmDCotLaO8/ha33D7dUqCAhPmSSrWt1lJc73NXcPz8');
my $tex_command = tex();
my $pdftex_command = pdftex();
my $filename = 'document';
my $contents = &{'compile-to-tex'}();
my $output_directory = &{'output-dir'}();
chomp $output_directory;
my $temporary_directory = state_based_filename();
$temporary_directory =~ s+^.*/++;
$temporary_directory = "$output_directory/$temporary_directory";
$tex_command =~ s/__TEMPORARY_DIRECTORY__/$temporary_directory/g;
$tex_command =~ s+__INPUT_FILE__+$temporary_directory/$filename.tex+g;
$pdftex_command =~ s/__TEMPORARY_DIRECTORY__/$temporary_directory/g;
$pdftex_command =~ s+__INPUT_FILE__+$temporary_directory/$filename.tex+g;
mkdir $temporary_directory;
file::write("$temporary_directory/$filename.tex", $contents);
if (&{'table-of-contents'}()) {
print "First invocation of TeX:\n";
print "Second invocation of TeX:\n";
print "PDFTeX:\n";
} else {
print "PDFTeX:\n";
associate('data::pdf-output-file', my $result = "$temporary_directory/$filename.pdf", execute => 1);
meta::function('compile-to-tex', <<'__cvWyD1kaLfBVnSppeGw/hSpleoWxLARJQwP1tUKvebc');
my ($document) = document();
$document = &$_($document) for (grep /^unlit_converter::/, sort keys %data);
meta::function('cp', <<'__yn1SQkcEk6o+gnuCy3QGVFtQb2piaCoUdJPGUkLjpD4');
my ($from, $to) = @_;
$data{$to} = $data{$from} if $data{$from};
messages::error("No such attribute $from") unless $data{$from};
meta::function('create', <<'__YDNTuzkJSNUIk4tbdwxep6/rT8uGnceIj7rljM9gusc');
my ($name, $value) = @_;
messages::error("Attribute $name already exists.") if grep {$_ eq $name} keys %data;
if ($value) {
associate($name, $value);
} else {
edit ($name);
meta::function('e', <<'__VOcQy5WG275NZGlFODdYHYBe3oJ7/CHmTT/L9O1I6t0');
edit('data::document', extension => '.tex');
meta::function('edit', <<'__rAkSOSll0evjt/D0qmnz2M++ACqz6cPtN8TLTHdQUJE');
my ($name, %options) = @_;
my $meta_extension = join '', grep {
my $s = $_;
$s =~ s/\s.*$//;
$name =~ /$s/
} split /\n/, &{'meta-associations'}();
$meta_extension =~ s/^.*\s//;
chomp $meta_extension;
messages::error("Attribute $name does not exist.") unless grep {$_ eq $name} keys %data;
associate($name, invoke_editor_on($data{$name} || "# Attribute $name", %options, extension => $meta_extension),
execute => $name !~ /^internal::/ && $name !~ /^bootstrap::/);
delete $data{$name} if length($data{$name}) == 0;
meta::function('exists', <<'__bxU1sDtIh3+P1x0HuuY0f7sKHr9qNZUEl64m2fvwmDk');
my $name = shift;
grep {$_ eq $name} keys %data;
meta::function('export', <<'__I4eRG7HtLDZhFgrlr0QDrQO5MRwJYGFlotQsWQIE/d8');
# Exports data into a text file.
# export attr1 attr2 attr3 ... file.txt
my $name = pop @_;
my @attributes = @_;
if (@attributes) {
my $file = join "\n", map {cat($_)} @attributes;
file::write ($name, $file);
} else {
messages::error ("Not enough arguments");
meta::function('grab', <<'__sXs1aeJVBERH6nWE7ZpWiIO5Cg7fSBWcoscDg1DHzD8');
my ($filename, @attribute_names) = @_;
associate("implementation::$_", `$filename cat $_`) for @attribute_names;
meta::function('import', <<'__7f1Q36AcIJ8/OaaYPGUq10PPFTKnEF0CMxS56DRsjKk');
my $name = pop @_;
my @files = @_;
if (@files) {
my $files = join "", map {file::read ($_)} @files;
associate ($name, $files);
else {
associate($name, join('', <STDIN>));
meta::function('install-vim-highlighter', <<'__3qCLWaQi9Dp8Xjs/XCdFMcPXj4H4+DWldUwYv9/fAGs');
my $home = $ENV{'HOME'};
mkdir "$home/.vim";
mkdir "$home/.vim/syntax";
file::write("$home/.vim/syntax/cltex.vim", retrieve('data::vim-highlighter'));
The highlighter was created successfully. To have syntax highlighting activated
automatically, append this line to your .vimrc:
au BufRead,BufNewFile *.cltex set filetype=cltex
Alternately, you can run $0 update-vimrc.
meta::function('lock', <<'__pqf/HijyN91BWpnS+uWYip/mFhHhcd+M9/YdlYsvv9Y');
my (undef, undef, $mode) = stat $0;
chmod $mode & 0555, $0;
meta::function('ls', <<'__OlQOgKhp6VUThx34wxmM3MFkwsHfR/c8dqvu07z059E');
my $criteria = join '|', @_;
my @lines = grep /$criteria/, sort keys %externalized_functions;
my $length = 0;
$length >= length($_) or $length = length($_) for @lines;
my @new_lines = map($_ . ' ' x ($length + 2 - length($_)) . $externalized_functions{$_}, @lines);
join("\n", sort @new_lines);
meta::function('ls-a', <<'__S5EydJRu+YIfcFmTMz1ZHX0cANJq32bU5JnYCe1tWdk');
my $criteria = join '|', @_;
my @lines = grep(/$criteria/, sort keys %data);
my $length = 0;
my %inverses;
$inverses{$externalized_functions{$_}} = $_ for keys %externalized_functions;
$length >= length($_) or $length = length($_) for @lines;
my @new_lines = map($_ . ' ' x ($length + 2 - length($_)) . ($inverses{$_} || ''), @lines);
join "\n", @new_lines;
meta::function('make', <<'__8mAUcwqcvcEhgs6tEjHaIx3uF9QOC+9DAI7NmulvJ74');
meta::function('mv', <<'__ijyNZ8r34FVK0Ki9/Q0Irx5k9U0pZ+/frrdlu+qkEP4');
my ($from, $to) = @_;
messages::error("The '$from' attribute does not exist.") unless grep $from, keys %data;
associate($to, retrieve($from));
meta::function('new', <<'__FQjehdFg7T3T2iHPMlGp6nAnrKAsQUIK5CXW02wNnos');
meta::function('perl', <<'__Ojd593Fa9fx1Yx2XuPzK6WTUyxO70Nbmlbl9YRodUWA');
my $result = eval($_[0]);
$@ ? $@ : $result;
meta::function('pull', <<'__ZU6uOu7dBdjjoNdEL/U7yrjicOQR5OLFQAacjrKqSCg');
my ($class_name) = @_;
my @attributes = grep /^implementation::/, split /\n/, `$class_name ls-a`;
for (@attributes) {
print STDERR "Adding $_\n";
associate(basename($_), `$class_name cat "$_"`);
meta::function('push-state', <<'__ik0ofu7R8gHAKSmMjek79V+yfgdjdK5Jmtwf7h8SpJk');
push @{$transient{'states'} = $transient{'states'} || []}, {%data};
my $state_count = scalar @{$transient{'states'}};
"There are now $state_count states on the stack.";
meta::function('reload', <<'__GwQjnnfuj0xQlervDJ9EVWzdmdz+XL3Gq0i9rdejvzM');
execute($_) for (grep {! (/^internal::/ || /^bootstrap::/)} keys %data);
meta::function('render', <<'__FnElnVt/3KpagnCCpoXWJvm+mW+WFkRxKvTOxQyYOF4');
use File::Copy;
file::write('./js-in-ten-minutes.tex', &{'compile-to-tex'}());
copy &{'pdf-output-file'}(), './js-in-ten-minutes.pdf';
meta::function('rm', <<'__7BVECTVo/mcT5+edC70WPc6S1xCbzAeyUCfCjkKWlww');
for my $to_be_deleted (@_) {
messages::warning("$to_be_deleted does not exist") unless grep {$_ eq $to_be_deleted} keys %data;
delete @data{@_};
meta::function('save', <<'__uWXGnrQr+A7Cl0zcsDuiokbWAw1XdMdjeq9gDcTMJIw');
my $serialized_data = serialize();
my $final_state = state();
my (undef, $temporary_filename) = tempfile("$0." . 'X' x 32, OPEN => 0);
file::write($temporary_filename, $serialized_data);
chmod 0700, $temporary_filename;
my $observed_state = `perl $temporary_filename state`;
chomp $observed_state;
if ($observed_state ne $final_state) {
messages::error("The state of this object ($final_state) is inconsistent with the state of $temporary_filename ($observed_state).\n" .
"$0 has not been updated.");
} else {
eval {file::write($0, $serialized_data)};
warn $@ if $@;
my $observed_self_state = `perl $0 state`;
chomp $observed_self_state;
unlink $temporary_filename if $observed_self_state eq $final_state;
meta::function('serialize', <<'__KGiI48MlyG6RAVW5QYRK8y97y8tx+jeAwPlY5eDtMTw');
my @keys_without_internals = grep(!/^internal::/, sort keys %data);
join "\n", $data{'bootstrap::initialization'},
(grep {$_} (map {serialize::single(@_)} grep(/^meta::/, @keys_without_internals),
grep(!/^meta::/, @keys_without_internals),
grep(/^internal::/, sort keys %data))),
meta::function('shell', <<'__mzNaDzdnJhpI/Va1/nY8LTN9BQtfr77CFKIeK2GdIC0');
use Term::ReadLine;
my $term = new Term::ReadLine "$0 shell";
my $prompt = &{'name'}() . '$ ';
my $OUT = $term->OUT || \*STDOUT;
$term->Attribs->{attempted_completion_function} = \&complete;
while (defined ($_ = $term->readline($prompt))) {
my $command_line = $_;
my @args = grep length, split /\s+|("[^"\\]*(?:\\.)?")/o;
my $function_name = shift @args;
return if $function_name eq 'exit';
s/^"(.*)"$/\1/o, s/\\\\"/"/go for @args;
if ($function_name) {
if ($externalized_functions{$function_name}) {
my $result = eval {&{$function_name}(@args)};
messages::warning($@) if $@;
chomp $result;
print $OUT $result, "\n" unless $@;
} else {
messages::warning("Command not found: $function_name");
for my $watch (@{$transient{'watch_list'}}) {
print $OUT eval($watch), "\n";
print $OUT "Error evaluating watched expression $watch: $@\n" if $@;
$prompt = &{'name'}() . '$ ';
meta::function('size', <<'__lDGr6yVnDwcDWLkJH16MNukltjG2ypBSk/ktYb80h80');
meta::function('snapshot', <<'__qjqsCy4CTt88dIi7IWM+Varpb3GcHsYrFTxW7EwpLW0');
my ($name) = @_;
file::write(my $finalname = state_based_filename($name), serialize(), noclobber => 1);
chmod 0700, $finalname;
meta::function('state', <<'__1S8nzRSMoxJU/VEv2rx/NrAt1iRgXQ9ugxjUP3IFunI');
sha256_base64 serialize();
meta::function('unlock', <<'__08PohCY8fcNe+pWCO6ic6XOOKv48NkrxpNMmTOUIFdA');
my (undef, undef, $mode) = stat $0;
chmod $mode | 0200, $0;
meta::function('update-from', <<'__jJ2tfSk/Quz8/1PiYxIwQjBRt/hYg51iJhAdxcdaHkM');
# Upgrade all attributes that aren't customized. In this case, we want everything except for configuration::, code::, and attribute::.
return "That is a really bad idea." if $0 =~ /\.\/(.*)/ && $_[0] eq $1 || $_[0] eq $0;
my @attributes = map {s/\s+.*//; $_} split(/\n/, qx|$_[0] ls-a|);
terminal::message('info', 'Replicating state...');
terminal::message('info', 'Updating meta attributes...');
for my $attribute (grep length && /^meta::/, @attributes) {
associate($attribute, join('', qx|$_[0] cat $attribute|));
print '.';
print "\n";
terminal::message('info', 'Updating non-meta attributes...');
for my $attribute (grep length && ! (/^configuration::/ || /^code::/ || /^attribute::/ || /^function::pop-state$/ ||
/^list::/ || /^issue::/ || /^data::/ || /^meta::datatypes/), @attributes) {
associate($attribute, join('', qx|$_[0] cat $attribute|));
reload(); # Necessary to activate new datatypes.
print '.';
print "\n";
terminal::message('info', 'Reloading new configuration');
terminal::message('info', "Imported from $_[0]. Run pop-state to undo this change.");
meta::function('update-vimrc', <<'__QweSkvdteATxlMZfDti3sl2iWNE+LbkHc1E7FkqNuCg');
open my $fh, '>>', "$ENV{'HOME'}/.vimrc";
print $fh "au BufRead,BufNewFile *.cltex set filetype=cltex";
close $fh;
meta::function('usage', <<'__oHVev4RtZlF/82SSE87y4Bf7ran2afn/HDtukOQBf9I');
<<"EOD" . join ' ', split /\n/, ls ();
Usage: $0 [options] action [arguments]
Defined actions:
meta::function('view', <<'__wEtlK5H0ttR24UvcFUsgg5Es1V/VbjMJlU+SKiO2jKs');
my $pdf_reader = &{'pdf-reader'}();
my $pdf_output_file = &{'pdf-output-file'}();
chomp $pdf_reader;
system("$pdf_reader '$pdf_output_file'");
meta::function('vim', <<'__1EcCMR8Tks8HBoOg+zAKJ4LlrRIY8nvLs4M1VTr2Zec');
# Installs VIM highlighters.
file::write("$ENV{'HOME'}/.vim/syntax/$_.vim", retrieve("vim_highlighter::$_")) for map {s/^vim_highlighter:://o; $_} grep /^vim_highlighter::/, sort keys %data;
meta::internal_function('associate', <<'__D8BKmEFp/adiPPqPnXyMOzlsBMCmuZi62UpJWdoFg/0');
my ($name, $value, %options) = @_;
my $namespace = namespace($name);
messages::error("Namespace $namespace does not exist") unless grep {$_ eq $namespace} @data_types;
$data{$name} = $value;
execute($name) if $options{'execute'};
meta::internal_function('basename', <<'__T4JEqOUYjMzssdVwV/rdgAhvr0Vz9TQUo0noTdeBLxw');
my ($name) = @_;
$name =~ s/^[^:]*:://;
meta::internal_function('complete', <<'__lhlD80z2kvEUEeHPqLFw6JE8xUdXr6J5Q1gXHg4beHg');
my @functions = sort keys %externalized_functions;
my @attributes = sort keys %data;
sub match {
my ($text, @options) = @_;
my @matches = sort grep /^$text/, @options;
if (@matches == 0) {return undef;}
elsif (@matches == 1) {return $matches [0];}
elsif (@matches > 1) {return ((longest ($matches [0], $matches [@matches - 1])), @matches);}
sub longest {
my ($s1, $s2) = @_;
return substr ($s1, 0, length $1) if ($s1 ^ $s2) =~ /^(\0*)/;
return '';
# This is another way to implement autocompletion.
# my $attribs = $term->Attribs;
# $attribs->{completion_entry_function} = $attribs->{list_completion_function};
# $attribs->{completion_word} = [sort keys %data, sort keys %externalized_functions];
my ($text, $line) = @_;
if ($line =~ / /) {
# Start matching attribute names.
match ($text, @attributes);
} else {
# Start of line, so it's a function.
match ($text, @functions);
meta::internal_function('execute', <<'__Ge94WTpmLuqsMDappj5G/G2BKILAE0GjeCqAeHLW6fQ');
my ($name, %options) = @_;
my $namespace = namespace($name);
eval {&{"meta::$namespace"}(basename($name), retrieve($name))};
warn $@ if $@ && $options{'carp'};
meta::internal_function('file::read', <<'__ZxBqZsMZZRuLMQp8Sy//ZsoAvriDebjYLGAX7p7AxXg');
my $name = shift;
open my($handle), "<", $name;
my $result = join "", <$handle>;
close $handle;
meta::internal_function('file::write', <<'__+NhpMabvNL+hHZaTZwBoFx2IFa79cjOZwGxEXX+xG0o');
my ($name, $contents, %options) = @_;
die "Choosing not to overwrite file $name" if $options{'noclobber'} && -f $name;
open my($handle), ">", $name or die "Can't open $name for writing";
print $handle $contents;
close $handle;
meta::internal_function('invoke_editor_on', <<'__97Lgs5+qfyAu92Vv5GCVVSYgUgFhOKYkVYXlbWoUs6U');
my ($data, %options) = @_;
my $content_hash = sha256_base64($data);
my $editor = $options{'editor'} || $ENV{'VISUAL'} || $ENV{'EDITOR'} ||
messages::error('Either the $VISUAL or $EDITOR environment variable should be set to a valid editor.');
my $options = $options{'options'} || $ENV{'VISUAL_OPTS'} || $ENV{'EDITOR_OPTS'} || '';
my $extension = $options{'extension'} || '';
my (undef, $filename) = tempfile("$0." . ("X" x 32), OPEN => 0);
$filename .= $extension;
file::write($filename, $data);
system("$editor $options \"$filename\"");
my $result = file::read($filename);
unlink $filename;
meta::internal_function('messages::error', <<'__200qXouilOAQNa4NkmIj6l+Rvb49Jpy8yxvIX29NcK4');
my ($message) = @_;
die "$message\n";
meta::internal_function('messages::warning', <<'__DeU/1Klulk/y4fO+wtKt+liOmUKwCEYKM8BvtlXYXBc');
my ($message) = @_;
print "$message\n";
meta::internal_function('namespace', <<'__D7UfKyyYZ1slZZyaS28hIt8a68jkI3ELBaddROXOHug');
my ($name) = @_;
$name =~ s/::.*$//;
meta::internal_function('retrieve', <<'__Erqqkp11FEHKsitr0DEJZ6OCGDYAs+U6BSu4UvLvsFM');
meta::internal_function('serialize::single', <<'__lDBHaXpbrfER2envI2Ipy77IcdjUnlZou+rggaxsAWE');
my $name = shift || $_;
my $contents = $data{$name};
my $delimiter = "__" . sha256_base64 $contents;
my $meta_function_name = "meta::" . namespace($name);
my $invocation_name = basename $name;
"$meta_function_name('$invocation_name', <<'$delimiter');\n$contents\n$delimiter\n";
meta::internal_function('state_based_filename', <<'__zNSrihAkMKJG5spRYgcFdoNArFKig1u12gIp6gJ8pZw');
my ($name) = @_;
my $noise = $name || state();
$noise =~ s/\//-/g;
meta::library('terminal', <<'__bakqo5o+Exi5Kw4onujvi9Pu3zR9lQA8cPKfYj6L74Q');
# Functions for nice-looking terminal output.
package terminal;
use constant black => "0;0";
use constant red => "1;31";
use constant yellow => "1;33";
use constant green => "1;32";
use constant blue => "1;34";
use constant purple => "1;35";
use constant cyan => "1;36";
my %default_colors = (info => green);
my $longest_prefix = 0;
$longest_prefix = $longest_prefix < $_ ? $_ : $longest_prefix for map length, keys %default_colors;
sub message {
my ($prefix, $message) = @_;
my $color = $default_colors{$prefix};
my $padding = ' ' x ($longest_prefix - length $prefix);
print "${padding}[\033[${color}m$prefix\033[0;0m] $message\n";
meta::line_filter('convert_header_info', <<'__1jqqbjBcdOqh/3nTKZSDbW1AgEo4kg0dQ5HdRAgxyzE');
my ($line) = @_;
$line =~ s/^= (.*)$/\\title{$1}/;
$line =~ s/^a (.*)$/\\author{$1}/;
$line =~ s/^d (.*)$/\\date{$1}/;
my $document_header = '\begin{document}\maketitle';
$document_header .= '\tableofcontents' if &{'table-of-contents'}();
$line =~ s/^begin$/$document_header/;
meta::line_filter('convert_itemized_environments', <<'__crKVW6OfRA2nN2SGPWQ/DAD9NPUws+boS8dK4cf3X2I');
my ($line) = @_;
$line =~ s/^\s*\+ /\\item /;
$line =~ s/^\s*\+\[([^\]]*)\] /\\item[\1] /;
$line =~ s/^\s*e\[$/\\begin{enumerate}/;
$line =~ s/^\s*i\[$/\\begin{itemize}/;
$line =~ s/^\s*d\[$/\\begin{description}/;
$line =~ s/^\s*a\[$/\\begin{align*}/;
$line =~ s/^\s*\]e$/\\end{enumerate}/;
$line =~ s/^\s*\]i$/\\end{itemize}/;
$line =~ s/^\s*\]d$/\\end{description}/;
$line =~ s/^\s*\]a$/\\end{align*}/;
meta::line_filter('convert_sections', <<'__lmhXPw+0a86ufxG3kCALtSl8Raqrt+7YZomx7zISUbw');
my ($line) = @_;
my %indentation_levels = (
0 => '\section',
2 => '\subsection',
4 => '\subsubsection',
6 => '\paragraph',
8 => '\subparagraph');
if ($line =~ /^(\s*)- (.*)$/) {
my $section = $indentation_levels{length($1)} || die "Invalid indentation level:\n$_";
"${section} {${2}}";
} else {
meta::unlit_converter('append_footer', <<'__xZrf+gAwEiK7btRcm+mwC/qtHsXp2FQ/Z8ZCwMvSw4Q');
my ($document) = @_;
meta::unlit_converter('main', <<'__3D082OWBmT2cWw4D6ktyDGp1MHknawaTDW2B0gvZPyM');
my ($document) = @_;
my $sections_already_encountered = 0;
my $inside_code_block = 0;
my $code_block_indentation = 0;
my $code_section_name = '';
my $result = '';
for (split /\n/, $document) {
# Handle code blocks.
if (/^(\s*):\.$/) {
$inside_code_block = $code_block_indentation = 0;
for my $filter_name (grep /^code_filter::/, sort keys %data) {
$_ = &$filter_name($_, name => $code_section_name, indentation => $code_block_indentation, end => 1);
if ($inside_code_block) {
my $spaces_to_delete = ' ' x $code_block_indentation;
for my $filter_name (grep /^code_filter::/, sort keys %data) {
$_ = &$filter_name($_, name => $code_section_name, indentation => $code_block_indentation);
} else {
for my $filter_name (grep /^line_filter::/, sort keys %data) {
$_ = &$filter_name($_);
if (/^(\s*)::(\s.*)?$/) {
$inside_code_block = 1;
$code_block_indentation = length($1);
$code_section_name = $2;
for my $filter_name (grep /^code_filter::/, sort keys %data) {
$_ = &$filter_name($_, name => $code_section_name, indentation => $code_block_indentation, begin => 1);
$result .= "$_\n";
meta::unlit_converter('prepend_header', <<'__4YCmTeTBS/MGOkeIFkQRlLujLJ1g/qS0/0iCeanWkkw');
my ($document) = @_;
header() . "\n$document";
meta::internal('runtime', <<'__YPmIzwZkTg8URmPfjiwGRG4VDUF2ZCJqTEz+gjETYLQ');
my $initial_state = sha256_base64 serialize();
push @script_args, shift @ARGV while @ARGV && $ARGV[0] =~ /^-/;
my $default_action = retrieve('data::default-action');
chomp $default_action;
my $function_name = shift(@ARGV) || $default_action || 'usage';
$function_name = 'usage' unless $externalized_functions{$function_name};
my $result = &{$function_name}(@ARGV);
chomp $result;
print "$result\n" if $result;
my $serialized_data = serialize();
my $final_state = sha256_base64 $serialized_data;
save() unless $initial_state eq $final_state;
Jump to Line
Something went wrong with that request. Please try again.