From 6f8c917822d3bb3d6974367c0d4c02558459394c Mon Sep 17 00:00:00 2001 From: arduano Date: Thu, 30 May 2024 19:53:36 +1000 Subject: [PATCH] "isXXX" builtins (#133) Builtins for testing whether a value is of a certain type, should be relatively simple --- nixjs-rt/src/builtins.ts | 22 +++++---- nixjs-rt/src/lib.ts | 55 +++++++++++---------- nixjs-rt/src/utils.ts | 2 + src/tests/builtins.rs | 104 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 37 deletions(-) diff --git a/nixjs-rt/src/builtins.ts b/nixjs-rt/src/builtins.ts index 8e6d4a3..42812ec 100644 --- a/nixjs-rt/src/builtins.ts +++ b/nixjs-rt/src/builtins.ts @@ -5,18 +5,20 @@ import { typeMismatchError } from "./errors/typeError"; import { Attrset, EvalCtx, - EvalException, FALSE, Lambda, NULL, + NixBool, NixFloat, NixInt, NixList, + NixNull, NixString, NixType, NixTypeClass, Path, TRUE, + nixBoolFromJs, } from "./lib"; import { dirOf, isAbsolutePath, normalizePath } from "./utils"; @@ -385,39 +387,39 @@ export function getBuiltins() { }, isAttrs: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof Attrset); }, isBool: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof NixBool); }, isFloat: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof NixFloat); }, isFunction: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof Lambda); }, isInt: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof NixInt); }, isList: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof NixList); }, isNull: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof NixNull); }, isPath: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof Path); }, isString: (arg) => { - throw new Error("unimplemented"); + return nixBoolFromJs(arg instanceof NixString); }, length: (arg) => { diff --git a/nixjs-rt/src/lib.ts b/nixjs-rt/src/lib.ts index 1184e7d..9ca19be 100644 --- a/nixjs-rt/src/lib.ts +++ b/nixjs-rt/src/lib.ts @@ -139,7 +139,7 @@ export abstract class NixType { } and(rhs: NixType): NixBool { - return _nixBoolFromJs(this.asBoolean() && rhs.asBoolean()); + return nixBoolFromJs(this.asBoolean() && rhs.asBoolean()); } apply(param: NixType): NixType { @@ -183,11 +183,11 @@ export abstract class NixType { } implication(rhs: NixType): NixBool { - return _nixBoolFromJs(!this.asBoolean() || rhs.asBoolean()); + return nixBoolFromJs(!this.asBoolean() || rhs.asBoolean()); } invert(): NixBool { - return _nixBoolFromJs(!this.asBoolean()); + return nixBoolFromJs(!this.asBoolean()); } /** @@ -228,7 +228,7 @@ export abstract class NixType { } or(rhs: NixType): NixBool { - return _nixBoolFromJs(this.asBoolean() || rhs.asBoolean()); + return nixBoolFromJs(this.asBoolean() || rhs.asBoolean()); } select(attrPath: NixType[], defaultValue: NixType | undefined): NixType { @@ -333,7 +333,7 @@ export class NixBool extends NixType { if (!(rhs instanceof NixBool)) { return FALSE; } - return _nixBoolFromJs(this.value === rhs.value); + return nixBoolFromJs(this.value === rhs.value); } } @@ -392,7 +392,7 @@ export abstract class Attrset extends NixType implements Scope { } foundValue = foundValue.get(attrName); } - return _nixBoolFromJs(foundValue !== undefined); + return nixBoolFromJs(foundValue !== undefined); } /** @@ -669,10 +669,10 @@ export class NixFloat extends NixType { override eq(rhs: NixType): NixBool { rhs = rhs.toStrict(); if (rhs instanceof NixInt) { - return _nixBoolFromJs(this.value === rhs.number); + return nixBoolFromJs(this.value === rhs.number); } if (rhs instanceof NixFloat) { - return _nixBoolFromJs(this.value === rhs.value); + return nixBoolFromJs(this.value === rhs.value); } return FALSE; } @@ -680,10 +680,10 @@ export class NixFloat extends NixType { override less(rhs: NixType): NixBool { rhs = rhs.toStrict(); if (rhs instanceof NixInt) { - return _nixBoolFromJs(this.value < rhs.number); + return nixBoolFromJs(this.value < rhs.number); } if (rhs instanceof NixFloat) { - return _nixBoolFromJs(this.value < rhs.value); + return nixBoolFromJs(this.value < rhs.value); } return super.less(rhs); } @@ -773,10 +773,10 @@ export class NixInt extends NixType { override eq(rhs: NixType): NixBool { rhs = rhs.toStrict(); if (rhs instanceof NixInt) { - return _nixBoolFromJs(this.int64 === rhs.int64); + return nixBoolFromJs(this.int64 === rhs.int64); } if (rhs instanceof NixFloat) { - return _nixBoolFromJs(this.number === rhs.value); + return nixBoolFromJs(this.number === rhs.value); } return super.eq(rhs); } @@ -784,10 +784,10 @@ export class NixInt extends NixType { override less(rhs: NixType): NixBool { rhs = rhs.toStrict(); if (rhs instanceof NixInt) { - return _nixBoolFromJs(this.int64 < rhs.int64); + return nixBoolFromJs(this.int64 < rhs.int64); } if (rhs instanceof NixFloat) { - return _nixBoolFromJs(this.number < rhs.value); + return nixBoolFromJs(this.number < rhs.value); } return super.less(rhs); } @@ -894,7 +894,7 @@ export class NixList extends NixType { return TRUE; } } - return _nixBoolFromJs(this.values.length < rhs.values.length); + return nixBoolFromJs(this.values.length < rhs.values.length); } toJs(): NixType[] { @@ -916,7 +916,7 @@ export class NixList extends NixType { export class NixNull extends NixType { override eq(rhs: NixType): NixBool { - return _nixBoolFromJs(rhs.toStrict() instanceof NixNull); + return nixBoolFromJs(rhs.toStrict() instanceof NixNull); } toJs(): boolean { @@ -936,10 +936,6 @@ export class NixNull extends NixType { } } -export const NULL = new NixNull(); -export const TRUE = new NixBool(true); -export const FALSE = new NixBool(false); - export class NixString extends NixType { readonly value: string; @@ -968,7 +964,7 @@ export class NixString extends NixType { if (!(rhs instanceof NixString)) { return FALSE; } - return _nixBoolFromJs(this.value === rhs.value); + return nixBoolFromJs(this.value === rhs.value); } override less(rhs: NixType): NixBool { @@ -976,7 +972,7 @@ export class NixString extends NixType { if (!(rhs instanceof NixString)) { return super.less(rhs); } - return _nixBoolFromJs(this.value < rhs.value); + return nixBoolFromJs(this.value < rhs.value); } toJs(): string { @@ -1020,7 +1016,7 @@ export class Path extends NixType { if (!(rhs instanceof Path)) { return super.less(rhs); } - return _nixBoolFromJs(this.path < rhs.path); + return nixBoolFromJs(this.path < rhs.path); } asString(): string { @@ -1204,6 +1200,15 @@ export class Lambda extends NixType { } } +export const NULL = new NixNull(); +export const TRUE = new NixBool(true); +export const FALSE = new NixBool(false); + +// For creating a bool without allocating a new object. +export function nixBoolFromJs(value: boolean): NixBool { + return value ? TRUE : FALSE; +} + // Attrset: export function attrset(evalCtx: EvalCtx, entries: AttrsetBody): Attrset { return new LazyAttrset(evalCtx, false, entries); @@ -1334,10 +1339,6 @@ function _attrPathToValue( }); } -function _nixBoolFromJs(value: boolean): NixBool { - return value ? TRUE : FALSE; -} - function _buildGlobalScope() { const scope = new Map(); const builtins = _createBuiltinsAttrset(); diff --git a/nixjs-rt/src/utils.ts b/nixjs-rt/src/utils.ts index 6396920..4c3f2f8 100644 --- a/nixjs-rt/src/utils.ts +++ b/nixjs-rt/src/utils.ts @@ -1,3 +1,5 @@ +import { NixBool, NixNull } from "./lib"; + export function isAbsolutePath(path: string): boolean { return path.startsWith("/"); } diff --git a/src/tests/builtins.rs b/src/tests/builtins.rs index fe0a8f8..3d444c9 100644 --- a/src/tests/builtins.rs +++ b/src/tests/builtins.rs @@ -518,38 +518,142 @@ mod intersectAttrs { mod isAttrs { use super::*; + + #[test] + fn eval_true() { + assert_eq!( + eval_ok("builtins.isAttrs { a = 1; b = 2; }"), + Value::Bool(true) + ); + assert_eq!(eval_ok("builtins.isAttrs { a = 1; }"), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isAttrs 1"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isAttrs [ 1 2 ]"), Value::Bool(false)); + } } mod isBool { use super::*; + + #[test] + fn eval_true() { + assert_eq!(eval_ok("builtins.isBool true"), Value::Bool(true)); + assert_eq!(eval_ok("builtins.isBool false"), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isBool 1"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isBool [ 1 2 ]"), Value::Bool(false)); + } } mod isFloat { use super::*; + + #[test] + fn eval_true() { + assert_eq!(eval_ok("builtins.isFloat 1.0"), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isFloat { }"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isFloat true"), Value::Bool(false)); + } } mod isFunction { use super::*; + + #[test] + fn eval_true() { + assert_eq!(eval_ok("builtins.isFunction (x: x)"), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isFunction 1"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isFunction [ 1 2 ]"), Value::Bool(false)); + } } mod isInt { use super::*; + + #[test] + fn eval_true() { + assert_eq!(eval_ok("builtins.isInt 1"), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isInt { }"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isInt true"), Value::Bool(false)); + } } mod isList { use super::*; + + #[test] + fn eval_true() { + assert_eq!(eval_ok("builtins.isList [ 1 2 ]"), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isList { }"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isList true"), Value::Bool(false)); + } } mod isNull { use super::*; + + #[test] + fn eval_true() { + assert_eq!(eval_ok("builtins.isNull null"), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isNull { }"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isNull true"), Value::Bool(false)); + } } mod isPath { use super::*; + + #[test] + fn eval_true() { + assert_eq!(eval_ok("builtins.isPath ./foo"), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isPath { }"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isPath true"), Value::Bool(false)); + } } mod isString { use super::*; + + #[test] + fn eval_true() { + assert_eq!(eval_ok("builtins.isString \"foo\""), Value::Bool(true)); + } + + #[test] + fn eval_false() { + assert_eq!(eval_ok("builtins.isString { }"), Value::Bool(false)); + assert_eq!(eval_ok("builtins.isString true"), Value::Bool(false)); + } } mod length {