452 lines
10 KiB
TypeScript
452 lines
10 KiB
TypeScript
import { describe, test, expect } from 'bun:test'
|
|
import '../../testSetup'
|
|
|
|
describe('DotGet', () => {
|
|
test('readme.txt is Word when readme not in scope', () => {
|
|
expect('readme.txt').toMatchTree(`Word readme.txt`)
|
|
})
|
|
|
|
test('readme.txt is Word when used in function', () => {
|
|
expect('echo readme.txt').toMatchTree(`
|
|
FunctionCall
|
|
Identifier echo
|
|
PositionalArg
|
|
Word readme.txt`)
|
|
})
|
|
|
|
test('obj.prop is DotGet when obj is assigned', () => {
|
|
expect('obj = 5; obj.prop').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier obj
|
|
Eq =
|
|
Number 5
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot obj
|
|
Identifier prop
|
|
`)
|
|
})
|
|
|
|
test('function parameters are in scope within function body', () => {
|
|
expect('do config: config.path end').toMatchTree(`
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier config
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot config
|
|
Identifier path
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('parameters out of scope outside function', () => {
|
|
expect('do x: x.prop end; x.prop').toMatchTree(`
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier x
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot x
|
|
Identifier prop
|
|
keyword end
|
|
Word x.prop
|
|
`)
|
|
})
|
|
|
|
test('multiple parameters work correctly', () => {
|
|
expect(`do x y:
|
|
x.foo
|
|
y.bar
|
|
end`).toMatchTree(`
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier x
|
|
Identifier y
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot x
|
|
Identifier foo
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot y
|
|
Identifier bar
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('nested functions with scope isolation', () => {
|
|
expect(`do x:
|
|
x.outer
|
|
do y: y.inner end
|
|
end`).toMatchTree(`
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier x
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot x
|
|
Identifier outer
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier y
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot y
|
|
Identifier inner
|
|
keyword end
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('dot get works as function argument', () => {
|
|
expect('config = 42; echo config.path').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier config
|
|
Eq =
|
|
Number 42
|
|
FunctionCall
|
|
Identifier echo
|
|
PositionalArg
|
|
DotGet
|
|
IdentifierBeforeDot config
|
|
Identifier path
|
|
`)
|
|
})
|
|
|
|
test('dot get works as bare function', () => {
|
|
expect('io = dict print=echo; io.print').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier io
|
|
Eq =
|
|
FunctionCall
|
|
Identifier dict
|
|
NamedArg
|
|
NamedArgPrefix print=
|
|
Identifier echo
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot io
|
|
Identifier print
|
|
`)
|
|
})
|
|
|
|
test('dot get works as function w/ args', () => {
|
|
expect('io = dict print=echo; io.print heya').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier io
|
|
Eq =
|
|
FunctionCall
|
|
Identifier dict
|
|
NamedArg
|
|
NamedArgPrefix print=
|
|
Identifier echo
|
|
FunctionCall
|
|
DotGet
|
|
IdentifierBeforeDot io
|
|
Identifier print
|
|
PositionalArg
|
|
Identifier heya
|
|
`)
|
|
})
|
|
|
|
test('dot get works as function in parens', () => {
|
|
expect('io = dict print=echo; (io.print heya)').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier io
|
|
Eq =
|
|
FunctionCall
|
|
Identifier dict
|
|
NamedArg
|
|
NamedArgPrefix print=
|
|
Identifier echo
|
|
ParenExpr
|
|
FunctionCall
|
|
DotGet
|
|
IdentifierBeforeDot io
|
|
Identifier print
|
|
PositionalArg
|
|
Identifier heya
|
|
`)
|
|
})
|
|
|
|
test('mixed file paths and dot get', () => {
|
|
expect('config = 42; cat readme.txt; echo config.path').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier config
|
|
Eq =
|
|
Number 42
|
|
FunctionCall
|
|
Identifier cat
|
|
PositionalArg
|
|
Word readme.txt
|
|
FunctionCall
|
|
Identifier echo
|
|
PositionalArg
|
|
DotGet
|
|
IdentifierBeforeDot config
|
|
Identifier path
|
|
`)
|
|
})
|
|
|
|
test("dot get doesn't work with spaces", () => {
|
|
expect('obj . prop').toMatchTree(`
|
|
FunctionCall
|
|
Identifier obj
|
|
PositionalArg
|
|
Word .
|
|
PositionalArg
|
|
Identifier prop`)
|
|
})
|
|
|
|
test('readme.1 is Word when readme not in scope', () => {
|
|
expect('readme.1').toMatchTree(`Word readme.1`)
|
|
})
|
|
|
|
test('readme.1 is Word when used in function', () => {
|
|
expect('echo readme.1').toMatchTree(`
|
|
FunctionCall
|
|
Identifier echo
|
|
PositionalArg
|
|
Word readme.1`)
|
|
})
|
|
|
|
test('obj.1 is DotGet when obj is assigned', () => {
|
|
expect('obj = 5; obj.1').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier obj
|
|
Eq =
|
|
Number 5
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot obj
|
|
Number 1
|
|
`)
|
|
})
|
|
|
|
test('obj.1 arg is DotGet when obj is assigned', () => {
|
|
expect('obj = 5; obj.1').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier obj
|
|
Eq =
|
|
Number 5
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot obj
|
|
Number 1
|
|
`)
|
|
})
|
|
|
|
test('dot get index works as function w/ args', () => {
|
|
expect(`io = list (do x: echo x end); io.0 heya`).toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier io
|
|
Eq =
|
|
FunctionCall
|
|
Identifier list
|
|
PositionalArg
|
|
ParenExpr
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier x
|
|
colon :
|
|
FunctionCall
|
|
Identifier echo
|
|
PositionalArg
|
|
Identifier x
|
|
keyword end
|
|
FunctionCall
|
|
DotGet
|
|
IdentifierBeforeDot io
|
|
Number 0
|
|
PositionalArg
|
|
Identifier heya
|
|
`)
|
|
})
|
|
|
|
test('can use the result of a parens expression as the property of dot get', () => {
|
|
expect('obj = list 1 2 3; obj.(1 + 2)').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier obj
|
|
Eq =
|
|
FunctionCall
|
|
Identifier list
|
|
PositionalArg
|
|
Number 1
|
|
PositionalArg
|
|
Number 2
|
|
PositionalArg
|
|
Number 3
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot obj
|
|
ParenExpr
|
|
BinOp
|
|
Number 1
|
|
Plus +
|
|
Number 2
|
|
`)
|
|
})
|
|
|
|
// NOTE: these are parsed as DotGet(meta, DotGet(script, name)) because that's easiest,
|
|
// but the compiler flattens them
|
|
test('chained dot get: meta.script.name', () => {
|
|
expect('meta = 42; meta.script.name').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier meta
|
|
Eq =
|
|
Number 42
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot meta
|
|
DotGet
|
|
IdentifierBeforeDot script
|
|
Identifier name
|
|
`)
|
|
})
|
|
|
|
test('chained dot get: a.b.c.d', () => {
|
|
expect('a = 1; a.b.c.d').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier a
|
|
Eq =
|
|
Number 1
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot a
|
|
DotGet
|
|
IdentifierBeforeDot b
|
|
DotGet
|
|
IdentifierBeforeDot c
|
|
Identifier d
|
|
`)
|
|
})
|
|
|
|
test('chained dot get in function call', () => {
|
|
expect('config = 1; echo config.db.host').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier config
|
|
Eq =
|
|
Number 1
|
|
FunctionCall
|
|
Identifier echo
|
|
PositionalArg
|
|
DotGet
|
|
IdentifierBeforeDot config
|
|
DotGet
|
|
IdentifierBeforeDot db
|
|
Identifier host
|
|
`)
|
|
})
|
|
|
|
test('chained dot get with numeric index at end', () => {
|
|
expect('obj = 1; obj.items.0').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier obj
|
|
Eq =
|
|
Number 1
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot obj
|
|
DotGet
|
|
IdentifierBeforeDot items
|
|
Number 0
|
|
`)
|
|
})
|
|
|
|
test('chained dot get with ParenExpr at end', () => {
|
|
expect('obj = 1; obj.items.(i)').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier obj
|
|
Eq =
|
|
Number 1
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot obj
|
|
DotGet
|
|
IdentifierBeforeDot items
|
|
ParenExpr
|
|
FunctionCallOrIdentifier
|
|
Identifier i
|
|
`)
|
|
})
|
|
|
|
test('not in scope remains Word with chained dots', () => {
|
|
expect('readme.md.bak').toMatchTree(`Word readme.md.bak`)
|
|
})
|
|
|
|
test('chained dot get in nested functions', () => {
|
|
expect(`do cfg:
|
|
do inner:
|
|
cfg.db.host
|
|
end
|
|
end`).toMatchTree(`
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier cfg
|
|
colon :
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier inner
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot cfg
|
|
DotGet
|
|
IdentifierBeforeDot db
|
|
Identifier host
|
|
keyword end
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('mixed simple and chained dot get', () => {
|
|
expect('obj = 1; obj.a; obj.b.c').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier obj
|
|
Eq =
|
|
Number 1
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot obj
|
|
Identifier a
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot obj
|
|
DotGet
|
|
IdentifierBeforeDot b
|
|
Identifier c
|
|
`)
|
|
})
|
|
|
|
test.skip('chained numeric dot get: row.2.1.b', () => {
|
|
expect('row = []; row.2.1').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier row
|
|
Eq =
|
|
Array []
|
|
FunctionCallOrIdentifier
|
|
DotGet
|
|
IdentifierBeforeDot row
|
|
DotGet
|
|
Number 2
|
|
DotGet
|
|
Number 1
|
|
Identifier b
|
|
`)
|
|
})
|
|
})
|