Kiesel Devlog #2: Iterators, more math, and a bug in the Zig stdlib
Published on 2023-09-15.Welcome to the second devlog for the Kiesel JS engine! It hasn't been long since the last one, but I worked on a couple of fun things since then :^)
Iterators
Quoting myself from last week:
[…] two major features needed to unlock a bunch of runtime functionality are promises and iterators, so I'll likely work on those soon.
And that's what I did the next day. Turns out iterators are much simpler than I remembered — the entire implementation is less than 300 lines!
As expected this unlocked a few more builtins which I did next:
- Array iterators (both
@@iterator
and thekeys()
/values()
/entries()
prototype functions) - String iterators (no key/value concept here, only
@@iterator
giving back code points) Array.from()
, which can collect items either from an iterable object or a plain array-like object- The
AggregateError
constructor, which is commonly called with an array of errors but accepts an arbitrary iterable object
More Math
Functions
I only implemented about half of the Math
object, so a bunch of functions were still missing. I picked mostly trigonometric and logarithmic functions next, all of which have an equivalent in Zig's std.math
library (or even compiler intrinsics) and thus were easy to implement.
Conveniently they are directly mirroring their equivalents from the standard C library in terms of behavior for "special" inputs (NaN, infinity, negative zero), as do the ones in ECMAScript.
This means that most steps can be folded into a single function call, e.g. for Math.sin()
:
/// 21.3.2.30 Math.sin ( x )
/// https://tc39.es/ecma262/#sec-math.sin
fn sin(agent: *Agent, _: Value, arguments: ArgumentsList) !Value {
const x = arguments.get(0);
// 1. Let n be ? ToNumber(x).
const n = try x.toNumber(agent);
// 2. If n is one of NaN, +0𝔽, or -0𝔽, return n.
// 3. If n is either +∞𝔽 or -∞𝔽, return NaN.
// 4. Return an implementation-approximated Number value representing the result of the
// sine of ℝ(n).
return Value.from(@sin(n.asFloat()));
}
While doing this I noticed two functions misbehaving in a subtle way: both std.math.asinh()
and std.math.cbrt()
would return 0
for -0
even though they are supposed to preserve the sign. I filed issues #17111 and #17112, which promptly got fixed in PRs #17120 and #17121 🎉
This is of course much less likely to happen in mature language and library implementations, but I'm more than happy to be an "early adopter" and improve the language for everyone by reporting/fixing such issues :^)
Pretty-Printing in Scripts
The Kiesel REPL already implements basic pretty-printing, generally trying to print relevant internal object state instead of just serializing (enumerable) properties.
I decided to expose this functionality to scripts — similar to pprint.pprint()
in Python — by adding an optional second argument for options to the non-standard Kiesel.print()
builtin:
Kiesel.print(value, { pretty: true });
Summary
test262 went up exactly 1%, now at 26%. CanadaHonk also prepared data collection for making a graph of progress over time for each engine, which I'm really looking forward to!
Expand to see the full list of newly implemented builtins 📝
AggregateError()
AggregateError.prototype.message
AggregateError.prototype.name
Array.from()
Array.prototype.entries()
Array.prototype.filter()
Array.prototype.keys()
Array.prototype.shift()
Array.prototype.unshift()
Array.prototype.values()
Array.prototype[@@iterator]()
Math.acos()
Math.acosh()
Math.asin()
Math.asinh()
Math.atan()
Math.atanh()
Math.cbrt()
Math.cos()
Math.cosh()
Math.exp()
Math.expm1()
Math.log()
Math.log10()
Math.log1p()
Math.log2()
Math.sin()
Math.sinh()
Math.sqrt()
Math.tan()
Math.tanh()
String.prototype.at()
String.prototype[@@iterator]()
There also were a couple of people reaching out about how to file issues or contribute after the previous devlog ended up on Lobsters. The short version is that I'm treating this as a read-only open source project for the moment — the source code is freely available, and everyone is welcome to use and modify it. However I want to avoid falling back into a mode where I spend more time reviewing PRs and handling issues than making actual progress myself.
There are plenty of issues that I'm aware of right now, and more that I'm not currently aware of, but this isn't the time for a backlog. Feel free to reach out directly if you have any questions/suggestions/fixes for obvious mistakes (example). :^)