Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

from ... import/export (reversed import/export) #1484

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions civet.dev/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2219,6 +2219,8 @@ class Civet <: Animal, Named

### Decorators

Civet uses [`@` for `this](#this), so decorators need to use `@@`:

<Playground>
@@Object.seal
class Civet
Expand Down Expand Up @@ -2503,12 +2505,12 @@ If you have `from` in your `import`, you can omit `import`.
You can also omit quotes around most filenames.

<Playground>
fs from fs
fs from fs/promises
{basename, dirname} from path
metadata from ./package.json with type: 'json'
</Playground>

### Import Like Object Destructuring
### Import-Like Object Destructuring

<Playground>
import {X: LocalX, Y: LocalY} from "./util"
Expand Down Expand Up @@ -2566,6 +2568,18 @@ Most declarations can also be `export default`:
export default x := 5
</Playground>

### Backward Import/Export

Similar to Python, you can put `from` before `import`/`export`.
Furthermore, `from` is optional.
This can improve autocompletion behavior.

<Playground>
from fs/promises import { readFile, writeFile }
./util import * as util
./package.json with {type: 'json'} export { version }
</Playground>

## Comments

### JavaScript Comments
Expand Down
58 changes: 46 additions & 12 deletions source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,8 @@ CallExpression
# Import declaration as an expression
Import _ NamedImports __ FromClause ->
return dynamizeImportDeclarationExpression($0)
FromClause:from __:fws Import:i _:iws NamedImports:imports ->
return dynamizeImportDeclarationExpression([i, iws, imports, fws, from])
# Dynamic import(), with optional parentheses when not used at top level.
# (At top level, ImportDeclaration will match first.)
"import" ArgumentsWithTrailingMemberExpressions CallExpressionRest*:rest ->
Expand Down Expand Up @@ -5315,19 +5317,25 @@ ImportDeclaration
type: "ImportDeclaration",
children: [imp, $0.slice(1)],
}
Import __ TypeKeyword __ ImportClause:imports __ FromClause:from -> { type: "ImportDeclaration", ts: true, children: $0, imports, from }
Import __ Operator OperatorBehavior?:behavior __ OperatorNamedImports:imports __ FromClause:from ->
( ( Import __ ) / ImpliedImport ):i Operator OperatorBehavior?:behavior __:ws1 OperatorNamedImports:imports __:ws2 FromClause:from ->
imports.specifiers.forEach((spec) => {
state.operators.set(spec.binding.name, spec.behavior ?? behavior)
})
return {
type: "ImportDeclaration",
children: [$1, $2, trimFirstSpace($5), imports, $7, from],
// omit $3 = Operator and $4 = OperatorBehavior
children: [i, trimFirstSpace(ws1), imports, ws2, from],
// omit $2 = Operator and $3 = OperatorBehavior
imports,
from,
}
Import __ ( TypeKeyword __ )?:t ImportClause:imports __ FromClause:from ->
return {
type: "ImportDeclaration",
children: $0,
imports,
from,
ts: !!t,
}
Import __ ImportClause:imports __ FromClause:from -> { type: "ImportDeclaration", children: $0, imports, from }
Import __ ModuleSpecifier:module -> { type: "ImportDeclaration", children: $0, module }
# NOTE: Added import shorthand
# NOTE: Not adding $loc to source map here yet because it will point to the start of the identifier
Expand All @@ -5341,16 +5349,25 @@ ImportDeclaration
}
const children = [i, t, imports, w, from]
return { type: "ImportDeclaration", ts: !!t, children, imports, from }
ImpliedImport:i Operator OperatorBehavior?:behavior __ OperatorNamedImports:imports __ FromClause:from ->
# NOTE: [from] ... import ... reverse syntax
ImpliedFromClause:from __:fws Import:i __:iws Operator OperatorBehavior?:behavior __:ows OperatorNamedImports:imports ->
imports.specifiers.forEach((spec) => {
state.operators.set(spec.binding.name, spec.behavior ?? behavior)
})
return {
type: "ImportDeclaration",
children: [$1, trimFirstSpace($4), imports, $6, from],
// omit $2 = Operator and $3 = OperatorBehavior
children: [i, iws, trimFirstSpace(ows), imports, fws, from],
// omit Operator and OperatorBehavior
imports,
from,
}
ImpliedFromClause:from __:fws Import:i __:iws ( TypeKeyword __ )?:t ImportClause:imports ->
return {
type: "ImportDeclaration",
children: [ i, iws, t, imports, fws, from ],
imports,
from,
ts: !!t,
}

ImpliedImport
Expand Down Expand Up @@ -5419,6 +5436,15 @@ FromClause
if (!Array.isArray(module)) return $0
return [ $1, $2, ...module ]

ImpliedFromClause
( ( From __ ) / ImpliedFrom ) ModuleSpecifier:module ->
if (!Array.isArray(module)) return $0
return [ $1, ...module ]

ImpliedFrom
"" ->
return { $loc, token: "from " }

# https://github.com/tc39/proposal-import-assertions
# https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#import-assertions
# https://github.com/tc39/proposal-import-attributes
Expand Down Expand Up @@ -5531,10 +5557,11 @@ UnprocessedModuleSpecifier
UnquotedSpecifier

UnquotedSpecifier
# Currently allowing all characters except for whitespace, double quotes, and semi-colon
# Currently allowing most non-whitespace characters
# Forbidding ; (statement separator), = (assignment), > (function arrow)
# It may make sense to restrict this to only allow characters that are valid in a module specifier
# Also consider URLs
/[^;"\s]+/:spec ->
/[^;"\s=>]+/:spec ->
return { $loc, token: `"${spec}"` }

# https://262.ecma-international.org/#prod-ImportedBinding
Expand Down Expand Up @@ -5583,8 +5610,15 @@ ExportDeclaration
# NOTE: Using Expression to allow If/Switch expressions
Decorators? Export __ Default __ ( HoistableDeclaration / ClassDeclaration / InterfaceDeclaration / MaybeNestedExpression ):declaration ->
return { type: "ExportDeclaration", declaration, ts: declaration.ts, children: $0 }
Export __ ExportFromClause __ FromClause ->
return { type: "ExportDeclaration", ts: $3.ts, children: $0 }
Export __ ExportFromClause:exports __ FromClause ->
return { type: "ExportDeclaration", ts: exports.ts, children: $0 }
# NOTE: [from] ... export ... reverse syntax
ImpliedFromClause:from __:fws Export:e __:ews ExportFromClause:exports ->
return {
type: "ExportDeclaration",
ts: exports.ts,
children: [ e, ews, exports, " ", from, trimFirstSpace(fws) ],
}
# NOTE: Declaration and VariableStatement should come before NamedExports
# so that NamedExports doesn't grab function, async, type, var, etc.
# NOTE: TS decorators come before `export` keyword but TC39 stage 3 decorators proposal come after
Expand Down
45 changes: 45 additions & 0 deletions test/export.civet
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,48 @@ describe "export", ->
---
export { s } from "./stuff.json" with {type: "json"}
"""

describe "from ... export", ->
testCase """
top-level explicit from
---
from "./module" export {x}
from ./module export {y}
from node:fs export { readFile }
from ./module export * as module
from ./module export a, b
from ./module export type T1, T2
from ./module export type { Type1, Type2 }
from ./package.json with {type: "json"} export { version }
---
export {x} from "./module"
export {y} from "./module"
export { readFile } from "node:fs"
export * as module from "./module"
export {a, b} from "./module"
export type {T1, T2} from "./module"
export type { Type1, Type2 } from "./module"
export { version } from "./package.json" with {type: "json"}
"""

testCase """
top-level implicit from
---
"./module" export {x}
./module export {y}
node:fs export { readFile }
./module export * as module
./module export a, b
./module export type T1, T2
./module export type { Type1, Type2 }
./package.json with {type: "json"} export { version }
---
export {x} from "./module"
export {y} from "./module"
export { readFile } from "node:fs"
export * as module from "./module"
export {a, b} from "./module"
export type {T1, T2} from "./module"
export type { Type1, Type2 } from "./module"
export { version } from "./package.json" with {type: "json"}
"""
107 changes: 107 additions & 0 deletions test/import.civet
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,110 @@ describe "import", ->
import "./foo.json"
${keyword}({type: "json"})
```

describe "from ... import", ->
testCase """
top-level explicit from
---
from "./module" import {x}
from ./module import {y}
from node:fs import { readFile }
from ./module import * as module
from ./module import moduleDefault
from ./module import type Type
from ./module import type { Type1, Type2 }
from ./package.json with {type: "json"} import { version }
from ./operators import operator {a, b}
---
import {x} from "./module"
import {y} from "./module"
import { readFile } from "node:fs"
import * as module from "./module"
import moduleDefault from "./module"
import type Type from "./module"
import type { Type1, Type2 } from "./module"
import { version } from "./package.json" with {type: "json"}
import {a, b} from "./operators"
"""

testCase """
top-level implicit from
---
"./module" import {x}
./module import {y}
node:fs import { readFile }
./module import * as module
./module import moduleDefault
./module import type Type
./module import type { Type1, Type2 }
./package.json with {type: "json"} import { version }
./operators import operator {a, b}
---
import {x} from "./module"
import {y} from "./module"
import { readFile } from "node:fs"
import * as module from "./module"
import moduleDefault from "./module"
import type Type from "./module"
import type { Type1, Type2 } from "./module"
import { version } from "./package.json" with {type: "json"}
import {a, b} from "./operators"
"""

testCase """
dynamic declaration explicit from
---
=>
from "./module" import {x}
from ./module import {y}
from node:fs import { readFile }
from ./module import * as module
from ./module import moduleDefault
from ./package.json with {type: "json"} import { version }
---
async () => {
const {x} = await import("./module")
const {y} = await import("./module")
const {readFile} = await import("node:fs")
const module = await import("./module")
const moduleDefault = (await import("./module")).default
const {version} = await import("./package.json", {with:{type: "json"}});return {version}
}
"""

testCase """
dynamic declaration implicit from
---
=>
"./module" import {x}
./module import {y}
node:fs import { readFile }
./module import * as module
./module import moduleDefault
./package.json with {type: "json"} import { version }
---
async () => {
const {x} = await import("./module")
const {y} = await import("./module")
const {readFile} = await import("node:fs")
const module = await import("./module")
const moduleDefault = (await import("./module")).default
const {version} = await import("./package.json", {with:{type: "json"}});return {version}
}
"""

throws """
mixed, from first
---
from x import y from z
---
ParseError
"""

throws """
mixed, import first
---
import x from y import z
---
ParseError
"""
Loading