312 lines
8.0 KiB
TypeScript
312 lines
8.0 KiB
TypeScript
import { describe } from 'bun:test'
|
|
import { expect, test } from 'bun:test'
|
|
|
|
describe('compiler', () => {
|
|
test('number literal', () => {
|
|
expect('42').toEvaluateTo(42)
|
|
})
|
|
|
|
test('negative number', () => {
|
|
expect('-5').toEvaluateTo(-5)
|
|
})
|
|
|
|
test('string literal', () => {
|
|
expect(`'hello'`).toEvaluateTo('hello')
|
|
})
|
|
|
|
test('boolean true', () => {
|
|
expect('true').toEvaluateTo(true)
|
|
})
|
|
|
|
test('boolean false', () => {
|
|
expect('false').toEvaluateTo(false)
|
|
})
|
|
|
|
test('addition', () => {
|
|
expect('2 + 3').toEvaluateTo(5)
|
|
})
|
|
|
|
test('subtraction', () => {
|
|
expect('10 - 4').toEvaluateTo(6)
|
|
})
|
|
|
|
test('multiplication', () => {
|
|
expect('3 * 4').toEvaluateTo(12)
|
|
})
|
|
|
|
test('division', () => {
|
|
expect('15 / 3').toEvaluateTo(5)
|
|
})
|
|
|
|
test('modulo', () => {
|
|
expect('44 % 2').toEvaluateTo(0)
|
|
expect('44 % 3').toEvaluateTo(2)
|
|
expect('3 % 4').toEvaluateTo(3)
|
|
})
|
|
|
|
test('assign number', () => {
|
|
expect('x = 5').toEvaluateTo(5)
|
|
})
|
|
|
|
test('emoji assignment to number', () => {
|
|
expect('💎 = 5; 💎').toEvaluateTo(5)
|
|
})
|
|
|
|
test('unbound identifier', () => {
|
|
expect('a = hello; a').toEvaluateTo('hello')
|
|
})
|
|
|
|
test('assign string', () => {
|
|
expect(`name = 'Alice'; name`).toEvaluateTo('Alice')
|
|
})
|
|
|
|
test('assign expression', () => {
|
|
expect('sum = 2 + 3; sum').toEvaluateTo(5)
|
|
})
|
|
|
|
test('array destructuring with two variables', () => {
|
|
expect('[ a b ] = [ 1 2 3 4 ]; a').toEvaluateTo(1)
|
|
expect('[ a b ] = [ 1 2 3 4 ]; b').toEvaluateTo(2)
|
|
})
|
|
|
|
test('array destructuring with one variable', () => {
|
|
expect('[ x ] = [ 42 ]; x').toEvaluateTo(42)
|
|
})
|
|
|
|
test('array destructuring with missing elements assigns null', () => {
|
|
expect('[ a b c ] = [ 1 2 ]; c').toEvaluateTo(null)
|
|
})
|
|
|
|
test('array destructuring returns the original array', () => {
|
|
expect('[ a b ] = [ 1 2 3 4 ]').toEvaluateTo([1, 2, 3, 4])
|
|
})
|
|
|
|
test('array destructuring with emoji identifiers', () => {
|
|
expect('[ 🚀 💎 ] = [ 1 2 ]; 🚀').toEvaluateTo(1)
|
|
expect('[ 🚀 💎 ] = [ 1 2 ]; 💎').toEvaluateTo(2)
|
|
})
|
|
|
|
test('parentheses', () => {
|
|
expect('(2 + 3) * 4').toEvaluateTo(20)
|
|
})
|
|
|
|
test('function', () => {
|
|
expect(`do a b: a + b end`).toEvaluateTo(Function)
|
|
})
|
|
|
|
test('function call', () => {
|
|
expect(`add = do a b: a + b end; add 2 9`).toEvaluateTo(11)
|
|
})
|
|
|
|
test('function call with named args', () => {
|
|
expect(`minus = do a b: a - b end; minus b=2 a=9`).toEvaluateTo(7)
|
|
})
|
|
|
|
test('function call with named and positional args', () => {
|
|
expect(`minus = do a b: a - b end; minus b=2 9`).toEvaluateTo(7)
|
|
expect(`minus = do a b: a - b end; minus 90 b=20`).toEvaluateTo(70)
|
|
expect(`minus = do a b: a - b end; minus a=900 200`).toEvaluateTo(700)
|
|
expect(`minus = do a b: a - b end; minus 2000 a=9000`).toEvaluateTo(7000)
|
|
})
|
|
|
|
test('function call with no args', () => {
|
|
expect(`bloop = do: 'bloop' end; bloop`).toEvaluateTo('bloop')
|
|
})
|
|
|
|
test('function call with if statement and multiple expressions', () => {
|
|
expect(`
|
|
abc = do:
|
|
if false:
|
|
echo nope
|
|
end
|
|
|
|
true
|
|
end
|
|
|
|
abc
|
|
`).toEvaluateTo(true)
|
|
})
|
|
|
|
test('simple conditionals', () => {
|
|
expect(`(3 < 6)`).toEvaluateTo(true)
|
|
expect(`(10 > 20)`).toEvaluateTo(false)
|
|
expect(`(4 <= 9)`).toEvaluateTo(true)
|
|
expect(`(15 >= 20)`).toEvaluateTo(false)
|
|
expect(`(7 == 7)`).toEvaluateTo(true)
|
|
expect(`(5 != 5)`).toEvaluateTo(false)
|
|
expect(`('shave' and 'haircut')`).toEvaluateTo('haircut')
|
|
expect(`(false and witness)`).toEvaluateTo(false)
|
|
expect(`('pride' or 'prejudice')`).toEvaluateTo('pride')
|
|
expect(`(false or false)`).toEvaluateTo(false)
|
|
})
|
|
|
|
test('if', () => {
|
|
expect(`if 3 < 9:
|
|
shire
|
|
end`).toEvaluateTo('shire')
|
|
})
|
|
|
|
test('if else', () => {
|
|
expect(`if false:
|
|
grey
|
|
else:
|
|
white
|
|
end`).toEvaluateTo('white')
|
|
})
|
|
|
|
test('if elseif', () => {
|
|
expect(`if false:
|
|
boromir
|
|
elseif true:
|
|
frodo
|
|
end`).toEvaluateTo('frodo')
|
|
})
|
|
|
|
test('if elseif else', () => {
|
|
expect(`if false:
|
|
destroyed
|
|
elseif true:
|
|
fire
|
|
else:
|
|
darkness
|
|
end`).toEvaluateTo('fire')
|
|
|
|
expect(`if false:
|
|
king
|
|
elseif false:
|
|
elf
|
|
elseif true:
|
|
dwarf
|
|
else:
|
|
scattered
|
|
end`).toEvaluateTo('dwarf')
|
|
})
|
|
|
|
test('single line if', () => {
|
|
expect(`if 3 < 9: shire end`).toEvaluateTo('shire')
|
|
})
|
|
})
|
|
|
|
describe('errors', () => {
|
|
test('syntax error', () => {
|
|
expect('2 + ').toFailEvaluation()
|
|
})
|
|
})
|
|
|
|
describe('multiline tests', () => {
|
|
test('multiline function', () => {
|
|
expect(`
|
|
add = do a b:
|
|
result = a + b
|
|
result
|
|
end
|
|
add 3 4
|
|
`).toEvaluateTo(7)
|
|
})
|
|
})
|
|
|
|
describe('string interpolation', () => {
|
|
test('string with variable interpolation', () => {
|
|
expect(`name = 'Alice'; 'hello $name'`).toEvaluateTo('hello Alice')
|
|
})
|
|
|
|
test('string with expression interpolation', () => {
|
|
expect(`'sum is $(2 + 3)'`).toEvaluateTo('sum is 5')
|
|
})
|
|
|
|
test('string with multiple interpolations', () => {
|
|
expect(`a = 10; b = 20; '$a + $b = $(a + b)'`).toEvaluateTo('10 + 20 = 30')
|
|
})
|
|
|
|
test('string with escape sequences', () => {
|
|
expect(`'line1\\nline2'`).toEvaluateTo('line1\nline2')
|
|
expect(`'tab\\there'`).toEvaluateTo('tab\there')
|
|
expect(`'back\\\\slash'`).toEvaluateTo('back\\slash')
|
|
})
|
|
|
|
test('string with escaped dollar sign', () => {
|
|
expect(`'price is \\$10'`).toEvaluateTo('price is $10')
|
|
})
|
|
|
|
test('string with mixed interpolation and escapes', () => {
|
|
expect(`x = 5; 'value: $x\\ntotal: $(x * 2)'`).toEvaluateTo('value: 5\ntotal: 10')
|
|
})
|
|
|
|
test('interpolation with unbound identifier', () => {
|
|
expect(`'greeting: $hello'`).toEvaluateTo('greeting: hello')
|
|
})
|
|
|
|
test('nested expression interpolation', () => {
|
|
expect(`a = 3; b = 4; 'result: $(a * (b + 1))'`).toEvaluateTo('result: 15')
|
|
})
|
|
})
|
|
|
|
describe('Regex', () => {
|
|
test('simple regex', () => {
|
|
expect('//hello//').toEvaluateTo(/hello/)
|
|
})
|
|
|
|
test('regex with flags', () => {
|
|
expect('//[a-z]+//gi').toEvaluateTo(/[a-z]+/gi)
|
|
})
|
|
|
|
test('regex in assignment', () => {
|
|
expect('pattern = //\\d+//; pattern').toEvaluateTo(/\d+/)
|
|
})
|
|
|
|
test('invalid regex pattern', () => {
|
|
expect('//[unclosed//').toEvaluateTo('//[unclosed//')
|
|
})
|
|
})
|
|
|
|
describe('native functions', () => {
|
|
test('print function', () => {
|
|
const add = (x: number, y: number) => x + y
|
|
expect(`add 5 9`).toEvaluateTo(14, { add })
|
|
})
|
|
})
|
|
|
|
describe('dot get', () => {
|
|
const array = (...items: any) => items
|
|
const dict = (atNamed: any) => atNamed
|
|
|
|
test('access array element', () => {
|
|
expect(`arr = array 'a' 'b' 'c'; arr.1`).toEvaluateTo('b', { array })
|
|
})
|
|
|
|
test('access dict element', () => {
|
|
expect(`dict = dict a=1 b=2; dict.a`).toEvaluateTo(1, { dict })
|
|
})
|
|
|
|
test('use parens expr with dot-get', () => {
|
|
expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array })
|
|
})
|
|
})
|
|
|
|
describe('default params', () => {
|
|
test('function with single default parameter', () => {
|
|
expect('add1 = do x=1: x + 1 end; add1').toEvaluateTo(2)
|
|
expect('add1 = do x=1: x + 1 end; add1 5').toEvaluateTo(6)
|
|
})
|
|
|
|
test('function with multiple default parameters', () => {
|
|
expect(`weird = do x='something' y=true: [x y] end; weird`).toEvaluateTo(['something', true])
|
|
})
|
|
|
|
test('function with mixed parameters', () => {
|
|
expect('multiply = do x y=5: x * y end; multiply 5').toEvaluateTo(25)
|
|
expect('multiply = do x y=5: x * y end; multiply 5 2').toEvaluateTo(10)
|
|
})
|
|
|
|
test.skip('array default', () => {
|
|
expect('abc = do alpha=[a b c]: alpha end; abc').toEvaluateTo(['a', 'b', 'c'])
|
|
expect('abc = do alpha=[a b c]: alpha end; abc [x y z]').toEvaluateTo(['x', 'y', 'z'])
|
|
})
|
|
|
|
test.skip('dict default', () => {
|
|
expect('make-person = do person=[name=Bob age=60]: person end; make-person')
|
|
.toEvaluateTo({ name: 'Bob', age: 60 })
|
|
expect('make-person = do person=[name=Bob age=60]: person end; make-person [name=Jon age=21]')
|
|
.toEvaluateTo({ name: 'Jon', age: 21 })
|
|
})
|
|
}) |