Parser 2.0 (Major Delezer) #52

Merged
defunkt merged 35 commits from parser2 into main 2025-12-08 16:35:34 +00:00
3 changed files with 88 additions and 16 deletions
Showing only changes of commit 566beb87ef - Show all commits

View File

@ -241,6 +241,22 @@ export class Parser {
// parse specific nodes
//
// raw determines whether we just want the SyntaxNodes or we want to
// wrap them in a PositionalArg
arg(raw = false): SyntaxNode {
// 'do' is a special function arg - it doesn't need to be wrapped
// in parens. otherwise, args are regular value()s
const val = this.is($T.Keyword, 'do') ? this.do() : this.value()
if (raw) {
return val
} else {
const arg = new SyntaxNode('PositionalArg', val.from, val.to)
arg.add(val)
return arg
}
}
// [ 1 2 3 ]
array(): SyntaxNode {
const open = this.expect($T.OpenBracket)
@ -425,10 +441,7 @@ export class Parser {
continue
}
if (this.is($T.NamedArgPrefix))
values.push(this.namedArg())
else
values.push(this.value())
values.push(this.is($T.NamedArgPrefix) ? this.namedArg() : this.arg())
}
const close = this.expect($T.CloseBracket)
@ -549,17 +562,8 @@ export class Parser {
const ident = fn ?? this.identifier()
const args: SyntaxNode[] = []
while (!this.isExprEnd() && !this.is($T.Operator, '|')) {
if (this.is($T.NamedArgPrefix)) {
args.push(this.namedArg())
} else {
// 'do' is the only keyword allowed as a function argument
const val = this.is($T.Keyword, 'do') ? this.do() : this.value()
const arg = new SyntaxNode('PositionalArg', val.from, val.to)
arg.add(val)
args.push(arg)
}
}
while (!this.isExprEnd() && !this.is($T.Operator, '|'))
args.push(this.is($T.NamedArgPrefix) ? this.namedArg() : this.arg())
const node = new SyntaxNode('FunctionCall', ident.from, (args.at(-1) || ident).to)
node.push(ident, ...args)
@ -669,7 +673,7 @@ export class Parser {
// abc= true
namedArg(): SyntaxNode {
const prefix = SyntaxNode.from(this.expect($T.NamedArgPrefix))
const val = this.value()
const val = this.arg(true)
const node = new SyntaxNode('NamedArg', prefix.from, val.to)
return node.push(prefix, val)
}

View File

@ -43,6 +43,58 @@ describe('calling functions', () => {
`)
})
test('call with function', () => {
expect(`tail do x: x end`).toMatchTree(`
FunctionCall
Identifier tail
PositionalArg
FunctionDef
Do do
Params
Identifier x
colon :
FunctionCallOrIdentifier
Identifier x
keyword end
`)
})
test('call with arg and function', () => {
expect(`tail true do x: x end`).toMatchTree(`
FunctionCall
Identifier tail
PositionalArg
Boolean true
PositionalArg
FunctionDef
Do do
Params
Identifier x
colon :
FunctionCallOrIdentifier
Identifier x
keyword end
`)
})
test('call with function in named arg', () => {
expect(`tail callback=do x: x end`).toMatchTree(`
FunctionCall
Identifier tail
NamedArg
NamedArgPrefix callback=
FunctionDef
Do do
Params
Identifier x
colon :
FunctionCallOrIdentifier
Identifier x
keyword end
`)
})
test('command with arg that is also a command', () => {
expect('tail tail').toMatchTree(`
FunctionCall

View File

@ -336,6 +336,22 @@ describe('dict literals', () => {
`)
})
test('work with functions', () => {
expect(`[trap=do x: x end]`).toMatchTree(`
Dict
NamedArg
NamedArgPrefix trap=
FunctionDef
Do do
Params
Identifier x
colon :
FunctionCallOrIdentifier
Identifier x
keyword end
`)
})
test('can be nested', () => {
expect('[a=one b=[two [c=three]]]').toMatchTree(`
Dict