import -> use

This commit is contained in:
Chris Wanstrath 2025-12-03 15:48:49 -08:00
parent ef20c67e61
commit 23642d3da1
7 changed files with 58 additions and 58 deletions

View File

@ -25,9 +25,9 @@ ${colors.bright}Commands:${colors.reset}
${colors.cyan}version${colors.reset} Print version
${colors.bright}Options:${colors.reset}
${colors.cyan}eval -I${colors.reset} ${colors.yellow}<module>${colors.reset} Import module (can be repeated)
Example: shrimp -I math -e 'random | echo'
Example: shrimp -Imath -Istr -e 'random | echo'`)
${colors.cyan}eval -U${colors.reset} ${colors.yellow}<module>${colors.reset} Use module (can be repeated)
Example: shrimp -U math -e 'random | echo'
Example: shrimp -Umath -Ustr -e 'random | echo'`)
}
function showVersion() {
@ -51,22 +51,22 @@ async function main() {
return
}
// Parse -I flags for imports (supports both "-I math" and "-Imath")
// Parse -U flags for use (supports both "-U math" and "-Umath")
const imports: string[] = []
while (args.length > 0) {
const arg = args[0]
if (arg === '-I') {
// "-I math" format
if (arg === '-U') {
// "-U math" format
if (args.length < 2) {
console.log(`${colors.bright}error: -I requires a module name${colors.reset}`)
process.exit(1)
}
imports.push(args[1])
args = args.slice(2)
} else if (arg.startsWith('-I')) {
// "-Imath" format
} else if (arg.startsWith('-U')) {
// "-Umath" format
const moduleName = arg.slice(2)
if (!moduleName) {
console.log(`${colors.bright}error: -I requires a module name${colors.reset}`)

View File

@ -496,29 +496,29 @@ describe('Compound assignment operators', () => {
})
})
describe('import', () => {
test('imports single dict', () => {
expect(`import str; starts-with? abc a`).toEvaluateTo(true)
describe('use', () => {
test('uses single dict', () => {
expect(`use str; starts-with? abc a`).toEvaluateTo(true)
})
test('imports multiple dicts', () => {
expect(`import str math list; map [1 2 3] do x: x * 2 end`).toEvaluateTo([2, 4, 6])
test('uses multiple dicts', () => {
expect(`use str math list; map [1 2 3] do x: x * 2 end`).toEvaluateTo([2, 4, 6])
})
test('imports non-prelude dicts', () => {
test('uses non-prelude dicts', () => {
expect(`
abc = [a=true b=yes c=si]
import abc
use abc
abc.b
`).toEvaluateTo('yes')
})
test('can specify imports', () => {
expect(`import str only=ends-with?; ref ends-with? | function?`).toEvaluateTo(true)
expect(`import str only=ends-with?; ref starts-with? | function?`).toEvaluateTo(false)
test('can specify uses', () => {
expect(`use str only=ends-with?; ref ends-with? | function?`).toEvaluateTo(true)
expect(`use str only=ends-with?; ref starts-with? | function?`).toEvaluateTo(false)
expect(`
abc = [a=true b=yes c=si]
import abc only=[a c]
use abc only=[a c]
[a c]
`).toEvaluateTo([true, 'si'])
})

View File

@ -57,7 +57,7 @@ export type NodeType =
| 'Star'
| 'Slash'
| 'Import'
| 'Use'
| 'Do'
| 'Underscore'
| 'colon'
@ -290,7 +290,7 @@ class SyntaxNodeType {
case 'Slash':
return term.Slash
case 'Import':
case 'Use':
return term.Import
case 'Do':

View File

@ -216,8 +216,8 @@ export class Parser {
if (this.is($T.Keyword, 'throw'))
return this.throw()
if (this.is($T.Keyword, 'import'))
return this.import()
if (this.is($T.Keyword, 'use'))
return this.use()
return this.expect($T.Keyword, 'if/while/do/import') as never
}
@ -705,27 +705,6 @@ export class Parser {
return node.push(this.keyword('end'))
}
import(): SyntaxNode {
const keyword = this.keyword('import')
const args: SyntaxNode[] = []
while (!this.isExprEnd()) {
if (this.is($T.NamedArgPrefix)) {
const prefix = SyntaxNode.from(this.next())
const val = this.value()
const arg = new SyntaxNode('NamedArg', prefix.from, val.to)
arg.push(prefix, val)
args.push(arg)
} else {
args.push(this.identifier())
}
}
const node = new SyntaxNode('Import', keyword.from, args.at(-1)!.to)
node.add(keyword)
return node.push(...args)
}
// if, while, do, etc
keyword(name: string): SyntaxNode {
const node = SyntaxNode.from(this.expect($T.Keyword, name))
@ -834,6 +813,27 @@ export class Parser {
return node.push(end)
}
use(): SyntaxNode {
const keyword = this.keyword('use')
const args: SyntaxNode[] = []
while (!this.isExprEnd()) {
if (this.is($T.NamedArgPrefix)) {
const prefix = SyntaxNode.from(this.next())
const val = this.value()
const arg = new SyntaxNode('NamedArg', prefix.from, val.to)
arg.push(prefix, val)
args.push(arg)
} else {
args.push(this.identifier())
}
}
const node = new SyntaxNode('Use', keyword.from, args.at(-1)!.to)
node.add(keyword)
return node.push(...args)
}
// while test: blah end
while(): SyntaxNode {
const keyword = this.keyword('while')

View File

@ -290,7 +290,7 @@ describe('operators', () => {
describe('keywords', () => {
test('keywords', () => {
expect(`import`).toMatchToken('Keyword', 'import')
expect(`use`).toMatchToken('Keyword', 'use')
expect(`end`).toMatchToken('Keyword', 'end')
expect(`do`).toMatchToken('Keyword', 'do')

View File

@ -2,19 +2,19 @@ import { expect, describe, test } from 'bun:test'
import '../shrimp.grammar' // Importing this so changes cause it to retest!
describe('import', () => {
test('parses single import', () => {
expect(`import str`).toMatchTree(`
Import
keyword import
describe('use', () => {
test('parses single use', () => {
expect(`use str`).toMatchTree(`
Use
keyword use
Identifier str
`)
})
test('parses multiple imports', () => {
expect(`import str math list`).toMatchTree(`
Import
keyword import
test('parses multiple uses', () => {
expect(`use str math list`).toMatchTree(`
Use
keyword use
Identifier str
Identifier math
Identifier list
@ -22,9 +22,9 @@ describe('import', () => {
})
test('parses named args', () => {
expect(`import str only=ends-with?`).toMatchTree(`
Import
keyword import
expect(`use str only=ends-with?`).toMatchTree(`
Use
keyword use
Identifier str
NamedArg
NamedArgPrefix only=

View File

@ -93,7 +93,7 @@ const operators = new Set([
])
const keywords = new Set([
'import',
'use',
'end',
'do',
'if',