JavaScript 2.0
Rationale
Units
previousupnext

Friday, September 20, 2002

Units derived from the Spice proposals had originally been part of the JavaScript 2.0 proposal but were deferred to a future version of the language in order to keep JavaScript 2.0 small. If implemented, units might behave as described in this section.

Lexing Units

A unit expression usually consists of a number followed by a unit name with no intervening white space, as in 7cm. The unit name can be quoted (for example, 7 "cm"), in which case white space is permitted between the number and the unit and the unit name can instead be a unit pattern (for example, 7 "Kg*cm^2/s").

When a numeric literal is immediately followed by an identifier, the lexer converts the identifier to a string literal. The parser then treats the number and string as a unit expression. The identifier cannot start with an underscore, but there are no reserved word restrictions on the identifier; any identifier that begins with a letter will work, even if it is a reserved word.

For example, 3in is converted to 3 "in". 5xena is converted to 5 "xena". On the other hand, 0xena is converted to 0xe "na". It is unwise to define unit names that begin with the letters e or E either alone or followed by a decimal digit, or x or X followed by a hexadecimal digit because of potential ambiguities with exponential or hexadecimal notation.

Unit Expressions

PrimaryExpression 
   null
|  true
|  false
|  public
|  Number
|  String
|  this
|  RegularExpression
|  UnitExpression
|  ArrayLiteral
|  ObjectLiteral
|  FunctionExpression
UnitExpression 
   ParenListExpression
|  Number [no line break] String
|  UnitExpression [no line break] String

A Number literal, ParenListExpression, or UnitExpression followed by a String literal is a unit expression, which is evaluated as follows:

Evaluate the String literal to obtain a string S. Parse that string according to the unit pattern grammar and semantics to obtain a list of identifiers and exponents [id1e1, id2e2, ..., idnen]. If the parse fails, signal a syntax error. If the list is empty, let U be the function nullUnit, which accepts one required and one optional argument and returns its first argument, ignoring the optional argument.

If the list is not empty, for each i between 1 and n, let Vi be the value of looking up idi in the system unit namespace. If ei is 1, let Fi = Vi; otherwise, let Fi = Vi.public::pow(ei). Then let U = F1*F2*...*Fn. For example, if S is "Kg*m^2/s^2*q", then U is the value of unit::Kg*unit::m.public::pow(2)*unit::s.public::pow(–2)*unit::q.public::pow(–1), where unit is the system unit namespace (the distinction between unit and the name unit is only relevant for perverse code that has a local definition named unit; the presence of such a local definition doesn’t affect unit lookup).

The result U should be callable as a function that accepts one required and one optional argument. The unit expression calls U, providing the numeric value of the Number literal, ParenListExpression, or UnitExpression as the first argument. The second argument is present only for the UnitExpression  Number [no line break] String production, in which case it is the original Number literal expressed as a string. Continuing the example above, the unit expression 32.50 "Kg*m^2/s^2*q", evaluates to the result of (unit::Kg*unit::m.public::pow(2)*unit::s.public::pow(–2)*unit::q.public::pow(–1))(32.5, "32.50").

U’s second argument allows user-defined unit classes to define extended syntaxes for numbers. For instance, a long-integer package might define a unit called "L" that treats the Number literal as a full 64-bit number without rounding it to a float64 first. Such a unit can be combined with other units by listing the units one after another; note that the lexer allows the first unit to be unquoted if it directly follows the number: 3L "cm" is the same as 3 "L" "cm" and evaluates to the result of unit::cm(unit::L(3, "3")).

Defining Units

Units are defined by placing them in the unit namespace, which is predefined by the system. Unit expressions are implicitly qualified using the unit namespace. The unit namespace is not used by default, so a program needs to explicitly qualify identifiers with unit:: to access existing unit definitions..

The easiest way to define new units is to scale, multiply, or divide existing ones. For example:

unit const µm = unit::m/1e6;
unit const Å = unit::m/1e10;
unit const dm = unit::m/10;
unit const \_in = unit::m*0.0254;
unit const liter = unit::dm.pow(3);

\_ must be used to define the unit in because in is a reserved word. However, the unit in may be used without quoting it, as in the expression 3in + 5cm.

Unit Class Extension

If class extensions were also added to the language, the class extension mechanism could be used instead of a unit namespace to define units. This way units could be designated as internal, private, etc.

The unit attribute would be defined as though

const unit = extend(Unit);

were evaluated at the top level. Unit would be the class that holds the definitions of unit names.


Waldemar Horwat
Last modified Friday, September 20, 2002
previousupnext