add number, string, boolean. closes #51
This commit is contained in:
parent
4da3c5ac06
commit
59b92714d2
|
|
@ -13,8 +13,9 @@ import { load } from './load'
|
||||||
import { list } from './list'
|
import { list } from './list'
|
||||||
import { math } from './math'
|
import { math } from './math'
|
||||||
import { str } from './str'
|
import { str } from './str'
|
||||||
|
import { types } from './types'
|
||||||
|
|
||||||
export const globals = {
|
export const globals: Record<string, any> = {
|
||||||
dict,
|
dict,
|
||||||
fs,
|
fs,
|
||||||
json,
|
json,
|
||||||
|
|
@ -34,7 +35,6 @@ export const globals = {
|
||||||
name: Bun.argv[2] || '(shrimp)',
|
name: Bun.argv[2] || '(shrimp)',
|
||||||
path: resolve(join('.', Bun.argv[2] ?? ''))
|
path: resolve(join('.', Bun.argv[2] ?? ''))
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// hello
|
// hello
|
||||||
|
|
@ -84,16 +84,6 @@ export const globals = {
|
||||||
exit: (num: number) => process.exit(num ?? 0),
|
exit: (num: number) => process.exit(num ?? 0),
|
||||||
|
|
||||||
// type predicates
|
// type predicates
|
||||||
'string?': (v: any) => toValue(v).type === 'string',
|
|
||||||
'number?': (v: any) => toValue(v).type === 'number',
|
|
||||||
'boolean?': (v: any) => toValue(v).type === 'boolean',
|
|
||||||
'array?': (v: any) => toValue(v).type === 'array',
|
|
||||||
'dict?': (v: any) => toValue(v).type === 'dict',
|
|
||||||
'function?': (v: any) => {
|
|
||||||
const t = toValue(v).type
|
|
||||||
return t === 'function' || t === 'native'
|
|
||||||
},
|
|
||||||
'null?': (v: any) => toValue(v).type === 'null',
|
|
||||||
'some?': (v: any) => toValue(v).type !== 'null',
|
'some?': (v: any) => toValue(v).type !== 'null',
|
||||||
|
|
||||||
// boolean/logic
|
// boolean/logic
|
||||||
|
|
@ -212,4 +202,8 @@ export function formatValue(value: Value, inner = false): string {
|
||||||
default:
|
default:
|
||||||
return String(value)
|
return String(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add types functions to top-level namespace
|
||||||
|
for (const [key, value] of Object.entries(types))
|
||||||
|
globals[key] = value
|
||||||
143
src/prelude/tests/types.test.ts
Normal file
143
src/prelude/tests/types.test.ts
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { expect, describe, test } from 'bun:test'
|
||||||
|
|
||||||
|
describe('type predicates', () => {
|
||||||
|
test('boolean? checks if value is boolean', async () => {
|
||||||
|
await expect(`boolean? true`).toEvaluateTo(true)
|
||||||
|
await expect(`boolean? false`).toEvaluateTo(true)
|
||||||
|
await expect(`boolean? 42`).toEvaluateTo(false)
|
||||||
|
await expect(`boolean? 'hello'`).toEvaluateTo(false)
|
||||||
|
await expect(`boolean? null`).toEvaluateTo(false)
|
||||||
|
await expect(`boolean? [1 2 3]`).toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('number? checks if value is number', async () => {
|
||||||
|
await expect(`number? 42`).toEvaluateTo(true)
|
||||||
|
await expect(`number? 3.14`).toEvaluateTo(true)
|
||||||
|
await expect(`number? 0`).toEvaluateTo(true)
|
||||||
|
await expect(`number? -5`).toEvaluateTo(true)
|
||||||
|
await expect(`number? 'hello'`).toEvaluateTo(false)
|
||||||
|
await expect(`number? true`).toEvaluateTo(false)
|
||||||
|
await expect(`number? null`).toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('string? checks if value is string', async () => {
|
||||||
|
await expect(`string? 'hello'`).toEvaluateTo(true)
|
||||||
|
await expect(`string? ''`).toEvaluateTo(true)
|
||||||
|
await expect(`string? world`).toEvaluateTo(true)
|
||||||
|
await expect(`string? 42`).toEvaluateTo(false)
|
||||||
|
await expect(`string? true`).toEvaluateTo(false)
|
||||||
|
await expect(`string? null`).toEvaluateTo(false)
|
||||||
|
await expect(`string? [1 2 3]`).toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('array? checks if value is array', async () => {
|
||||||
|
await expect(`array? [1 2 3]`).toEvaluateTo(true)
|
||||||
|
await expect(`array? []`).toEvaluateTo(true)
|
||||||
|
await expect(`array? ['a' 'b']`).toEvaluateTo(true)
|
||||||
|
await expect(`array? [a=1 b=2]`).toEvaluateTo(false)
|
||||||
|
await expect(`array? 42`).toEvaluateTo(false)
|
||||||
|
await expect(`array? 'hello'`).toEvaluateTo(false)
|
||||||
|
await expect(`array? null`).toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('list? is alias for array?', async () => {
|
||||||
|
await expect(`list? [1 2 3]`).toEvaluateTo(true)
|
||||||
|
await expect(`list? []`).toEvaluateTo(true)
|
||||||
|
await expect(`list? [a=1 b=2]`).toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dict? checks if value is dict', async () => {
|
||||||
|
await expect(`dict? [a=1 b=2]`).toEvaluateTo(true)
|
||||||
|
await expect(`dict? [=]`).toEvaluateTo(true)
|
||||||
|
await expect(`dict? [1 2 3]`).toEvaluateTo(false)
|
||||||
|
await expect(`dict? []`).toEvaluateTo(false)
|
||||||
|
await expect(`dict? 42`).toEvaluateTo(false)
|
||||||
|
await expect(`dict? 'hello'`).toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('function? checks if value is function', async () => {
|
||||||
|
await expect(`
|
||||||
|
my-fn = do x: x * 2 end
|
||||||
|
function? my-fn
|
||||||
|
`).toEvaluateTo(true)
|
||||||
|
await expect(`function? inc`).toEvaluateTo(true)
|
||||||
|
await expect(`function? list.map`).toEvaluateTo(true)
|
||||||
|
await expect(`function? 42`).toEvaluateTo(false)
|
||||||
|
await expect(`function? 'hello'`).toEvaluateTo(false)
|
||||||
|
await expect(`function? [1 2 3]`).toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('null? checks if value is null', async () => {
|
||||||
|
await expect(`null? null`).toEvaluateTo(true)
|
||||||
|
await expect(`null? 0`).toEvaluateTo(false)
|
||||||
|
await expect(`null? false`).toEvaluateTo(false)
|
||||||
|
await expect(`null? ''`).toEvaluateTo(false)
|
||||||
|
await expect(`null? []`).toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('type coercion', () => {
|
||||||
|
test('boolean coerces to boolean', async () => {
|
||||||
|
await expect(`boolean true`).toEvaluateTo(true)
|
||||||
|
await expect(`boolean false`).toEvaluateTo(false)
|
||||||
|
await expect(`boolean 1`).toEvaluateTo(true)
|
||||||
|
await expect(`boolean 0`).toEvaluateTo(false)
|
||||||
|
await expect(`boolean 'hello'`).toEvaluateTo(true)
|
||||||
|
await expect(`boolean ''`).toEvaluateTo(false)
|
||||||
|
await expect(`boolean null`).toEvaluateTo(false)
|
||||||
|
await expect(`boolean [1 2 3]`).toEvaluateTo(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('number coerces to number', async () => {
|
||||||
|
await expect(`number 42`).toEvaluateTo(42)
|
||||||
|
await expect(`number '42'`).toEvaluateTo(42)
|
||||||
|
await expect(`number '3.14'`).toEvaluateTo(3.14)
|
||||||
|
await expect(`number true`).toEvaluateTo(1)
|
||||||
|
await expect(`number false`).toEvaluateTo(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('string coerces to string', async () => {
|
||||||
|
await expect(`string 'hello'`).toEvaluateTo('hello')
|
||||||
|
await expect(`string 42`).toEvaluateTo('42')
|
||||||
|
await expect(`string true`).toEvaluateTo('true')
|
||||||
|
await expect(`string false`).toEvaluateTo('false')
|
||||||
|
await expect(`string null`).toEvaluateTo('null')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('type predicates in conditionals', () => {
|
||||||
|
test('using type predicates in if statements', async () => {
|
||||||
|
await expect(`
|
||||||
|
x = 42
|
||||||
|
if (number? x):
|
||||||
|
'is-num'
|
||||||
|
else:
|
||||||
|
'not-num'
|
||||||
|
end
|
||||||
|
`).toEvaluateTo('is-num')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('filtering by type', async () => {
|
||||||
|
await expect(`
|
||||||
|
items = [1 'hello' 2 'world' 3]
|
||||||
|
list.filter items number?
|
||||||
|
`).toEvaluateTo([1, 2, 3])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('filtering strings', async () => {
|
||||||
|
await expect(`
|
||||||
|
items = [1 'hello' 2 'world' 3]
|
||||||
|
list.filter items string?
|
||||||
|
`).toEvaluateTo(['hello', 'world'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('checking for functions', async () => {
|
||||||
|
await expect(`
|
||||||
|
double = do x: x * 2 end
|
||||||
|
not-fn = 42
|
||||||
|
is-fn = function? double
|
||||||
|
is-not-fn = function? not-fn
|
||||||
|
is-fn and (not is-not-fn)
|
||||||
|
`).toEvaluateTo(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
22
src/prelude/types.ts
Normal file
22
src/prelude/types.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { toValue } from 'reefvm'
|
||||||
|
|
||||||
|
export const types = {
|
||||||
|
'boolean?': (v: any) => toValue(v).type === 'boolean',
|
||||||
|
boolean: (v: any) => Boolean(v),
|
||||||
|
|
||||||
|
'number?': (v: any) => toValue(v).type === 'number',
|
||||||
|
number: (v: any) => Number(v),
|
||||||
|
|
||||||
|
'string?': (v: any) => toValue(v).type === 'string',
|
||||||
|
string: (v: any) => String(v),
|
||||||
|
|
||||||
|
|
||||||
|
'array?': (v: any) => toValue(v).type === 'array',
|
||||||
|
'list?': (v: any) => toValue(v).type === 'array',
|
||||||
|
|
||||||
|
'dict?': (v: any) => toValue(v).type === 'dict',
|
||||||
|
|
||||||
|
'function?': (v: any) => ['function', 'native'].includes(toValue(v).type),
|
||||||
|
|
||||||
|
'null?': (v: any) => toValue(v).type === 'null',
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user