New import syntax
This commit is contained in:
parent
54962e6dcd
commit
29b31c4a5a
@ -113,5 +113,5 @@ pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
|
||||
let source = Source::synthesized(text, span);
|
||||
let route = model::Route::default();
|
||||
let module = model::eval(vm.world(), route.track(), &source)?;
|
||||
Ok(Value::Content(module.content))
|
||||
Ok(Value::Content(module.content()))
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Category> {
|
||||
SyntaxKind::Return => Some(Category::Keyword),
|
||||
SyntaxKind::Import => Some(Category::Keyword),
|
||||
SyntaxKind::Include => Some(Category::Keyword),
|
||||
SyntaxKind::From => Some(Category::Keyword),
|
||||
SyntaxKind::As => Some(Category::Keyword),
|
||||
|
||||
SyntaxKind::Markup { .. }
|
||||
if node.parent_kind() == Some(&SyntaxKind::TermItem)
|
||||
@ -198,6 +198,17 @@ pub fn highlight(node: &LinkedNode) -> Option<Category> {
|
||||
| SyntaxKind::Frac,
|
||||
) => Some(Category::Interpolated),
|
||||
Some(SyntaxKind::FuncCall) => Some(Category::Function),
|
||||
Some(SyntaxKind::FieldAccess)
|
||||
if node
|
||||
.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.filter(|gp| gp.kind() == &SyntaxKind::Parenthesized)
|
||||
.and_then(|gp| gp.parent())
|
||||
.map_or(false, |ggp| ggp.kind() == &SyntaxKind::FuncCall)
|
||||
&& node.next_sibling().is_none() =>
|
||||
{
|
||||
Some(Category::Function)
|
||||
}
|
||||
Some(SyntaxKind::MethodCall) if node.prev_sibling().is_some() => {
|
||||
Some(Category::Function)
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ pub fn compile(world: &(dyn World + 'static), source: &Source) -> SourceResult<D
|
||||
let module = model::eval(world.track(), route.track(), source)?;
|
||||
|
||||
// Typeset the module's contents.
|
||||
model::typeset(world.track(), &module.content)
|
||||
model::typeset(world.track(), &module.content())
|
||||
}
|
||||
|
||||
/// The environment in which typesetting occurs.
|
||||
|
@ -9,7 +9,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use super::{
|
||||
methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Func, Label,
|
||||
LangItems, Recipe, Scope, Scopes, Selector, StyleMap, Transform, Value,
|
||||
LangItems, Module, Recipe, Scopes, Selector, StyleMap, Transform, Value,
|
||||
};
|
||||
use crate::diag::{
|
||||
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
|
||||
@ -17,7 +17,7 @@ use crate::diag::{
|
||||
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
|
||||
use crate::syntax::ast::AstNode;
|
||||
use crate::syntax::{ast, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode, Unit};
|
||||
use crate::util::{EcoString, PathExt};
|
||||
use crate::util::PathExt;
|
||||
use crate::World;
|
||||
|
||||
const MAX_ITERATIONS: usize = 10_000;
|
||||
@ -53,7 +53,7 @@ pub fn eval(
|
||||
}
|
||||
|
||||
// Assemble the module.
|
||||
Ok(Module { scope: vm.scopes.top, content: result? })
|
||||
Ok(Module::evaluated(source.path(), vm.scopes.top, result?))
|
||||
}
|
||||
|
||||
/// A virtual machine.
|
||||
@ -181,15 +181,6 @@ impl Route {
|
||||
}
|
||||
}
|
||||
|
||||
/// An evaluated module, ready for importing or typesetting.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module {
|
||||
/// The top-level definitions that were bound in this module.
|
||||
pub scope: Scope,
|
||||
/// The module's layoutable contents.
|
||||
pub content: Content,
|
||||
}
|
||||
|
||||
/// Evaluate an expression.
|
||||
pub(super) trait Eval {
|
||||
/// The output of evaluating the expression.
|
||||
@ -803,7 +794,15 @@ impl Eval for ast::FieldAccess {
|
||||
Value::Dict(dict) => dict.at(&field).at(span)?.clone(),
|
||||
Value::Content(content) => content
|
||||
.field(&field)
|
||||
.ok_or_else(|| format!("unknown field {field:?}"))
|
||||
.ok_or_else(|| format!("unknown field `{field}`"))
|
||||
.at(span)?,
|
||||
Value::Module(module) => module
|
||||
.scope()
|
||||
.get(&field)
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
format!("module `{}` does not contain `{field}`", module.name())
|
||||
})
|
||||
.at(span)?,
|
||||
v => bail!(
|
||||
self.target().span(),
|
||||
@ -1163,19 +1162,22 @@ impl Eval for ast::ModuleImport {
|
||||
type Output = Value;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
let span = self.path().span();
|
||||
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
|
||||
let module = import(vm, &path, span)?;
|
||||
let span = self.source().span();
|
||||
let source = self.source().eval(vm)?;
|
||||
let module = import(vm, source, span)?;
|
||||
|
||||
match self.imports() {
|
||||
ast::Imports::Wildcard => {
|
||||
for (var, value) in module.scope.iter() {
|
||||
None => {
|
||||
vm.scopes.top.define(module.name().clone(), module);
|
||||
}
|
||||
Some(ast::Imports::Wildcard) => {
|
||||
for (var, value) in module.scope().iter() {
|
||||
vm.scopes.top.define(var.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
ast::Imports::Items(idents) => {
|
||||
Some(ast::Imports::Items(idents)) => {
|
||||
for ident in idents {
|
||||
if let Some(value) = module.scope.get(&ident) {
|
||||
if let Some(value) = module.scope().get(&ident) {
|
||||
vm.scopes.top.define(ident.take(), value.clone());
|
||||
} else {
|
||||
bail!(ident.span(), "unresolved import");
|
||||
@ -1192,17 +1194,23 @@ impl Eval for ast::ModuleInclude {
|
||||
type Output = Content;
|
||||
|
||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||
let span = self.path().span();
|
||||
let path = self.path().eval(vm)?.cast::<EcoString>().at(span)?;
|
||||
let module = import(vm, &path, span)?;
|
||||
Ok(module.content)
|
||||
let span = self.source().span();
|
||||
let source = self.source().eval(vm)?;
|
||||
let module = import(vm, source, span)?;
|
||||
Ok(module.content())
|
||||
}
|
||||
}
|
||||
|
||||
/// Process an import of a module relative to the current location.
|
||||
fn import(vm: &Vm, path: &str, span: Span) -> SourceResult<Module> {
|
||||
fn import(vm: &Vm, source: Value, span: Span) -> SourceResult<Module> {
|
||||
let path = match source {
|
||||
Value::Str(path) => path,
|
||||
Value::Module(module) => return Ok(module),
|
||||
v => bail!(span, "expected path or module, found {}", v.type_name()),
|
||||
};
|
||||
|
||||
// Load the source file.
|
||||
let full = vm.locate(path).at(span)?;
|
||||
let full = vm.locate(&path).at(span)?;
|
||||
let id = vm.world.resolve(&full).at(span)?;
|
||||
|
||||
// Prevent cyclic importing.
|
||||
|
@ -443,8 +443,8 @@ impl<'a> CapturesVisitor<'a> {
|
||||
// An import contains items, but these are active only after the
|
||||
// path is evaluated.
|
||||
Some(ast::Expr::Import(expr)) => {
|
||||
self.visit(expr.path().as_untyped());
|
||||
if let ast::Imports::Items(items) = expr.imports() {
|
||||
self.visit(expr.source().as_untyped());
|
||||
if let Some(ast::Imports::Items(items)) = expr.imports() {
|
||||
for item in items {
|
||||
self.bind(item);
|
||||
}
|
||||
@ -525,8 +525,8 @@ mod tests {
|
||||
test("#for x in y {} #x", &["x", "y"]);
|
||||
|
||||
// Import.
|
||||
test("#import x, y from z", &["z"]);
|
||||
test("#import x, y, z from x + y", &["x", "y"]);
|
||||
test("#import z: x, y", &["z"]);
|
||||
test("#import x + y: x, y, z", &["x", "y"]);
|
||||
|
||||
// Blocks.
|
||||
test("{ let x = 1; { let y = 2; y }; x + y }", &["y"]);
|
||||
|
@ -19,6 +19,7 @@ mod content;
|
||||
mod eval;
|
||||
mod func;
|
||||
mod methods;
|
||||
mod module;
|
||||
mod ops;
|
||||
mod realize;
|
||||
mod scope;
|
||||
@ -36,6 +37,7 @@ pub use self::dict::*;
|
||||
pub use self::eval::*;
|
||||
pub use self::func::*;
|
||||
pub use self::library::*;
|
||||
pub use self::module::*;
|
||||
pub use self::realize::*;
|
||||
pub use self::scope::*;
|
||||
pub use self::str::*;
|
||||
|
62
src/model/module.rs
Normal file
62
src/model/module.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{Content, Scope};
|
||||
use crate::util::EcoString;
|
||||
|
||||
/// An evaluated module, ready for importing or typesetting.
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct Module(Arc<Repr>);
|
||||
|
||||
/// The internal representation.
|
||||
#[derive(Clone, Hash)]
|
||||
struct Repr {
|
||||
/// The module's name.
|
||||
name: EcoString,
|
||||
/// The top-level definitions that were bound in this module.
|
||||
scope: Scope,
|
||||
/// The module's layoutable contents.
|
||||
content: Content,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
/// Create a new, empty module with the given `name`.
|
||||
pub fn new(name: impl Into<EcoString>) -> Self {
|
||||
Self(Arc::new(Repr {
|
||||
name: name.into(),
|
||||
scope: Scope::new(),
|
||||
content: Content::empty(),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Create a new module from an evalauted file.
|
||||
pub fn evaluated(path: &Path, scope: Scope, content: Content) -> Self {
|
||||
let name = path.file_stem().unwrap_or_default().to_string_lossy().into();
|
||||
Self(Arc::new(Repr { name, scope, content }))
|
||||
}
|
||||
|
||||
/// Get the module's name.
|
||||
pub fn name(&self) -> &EcoString {
|
||||
&self.0.name
|
||||
}
|
||||
|
||||
/// Access the module's scope.
|
||||
pub fn scope(&self) -> &Scope {
|
||||
&self.0.scope
|
||||
}
|
||||
|
||||
/// Extract the module's content.
|
||||
pub fn content(self) -> Content {
|
||||
match Arc::try_unwrap(self.0) {
|
||||
Ok(repr) => repr.content,
|
||||
Err(arc) => arc.content.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Module {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "<module {}>", self.name())
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use std::sync::Arc;
|
||||
use siphasher::sip128::{Hasher128, SipHasher};
|
||||
|
||||
use super::{
|
||||
format_str, ops, Args, Array, Cast, CastInfo, Content, Dict, Func, Label, Str,
|
||||
format_str, ops, Args, Array, Cast, CastInfo, Content, Dict, Func, Label, Module, Str,
|
||||
};
|
||||
use crate::diag::StrResult;
|
||||
use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel, RgbaColor};
|
||||
@ -52,6 +52,8 @@ pub enum Value {
|
||||
Func(Func),
|
||||
/// Captured arguments to a function.
|
||||
Args(Args),
|
||||
/// A module.
|
||||
Module(Module),
|
||||
/// A dynamic value.
|
||||
Dyn(Dynamic),
|
||||
}
|
||||
@ -86,6 +88,7 @@ impl Value {
|
||||
Self::Dict(_) => Dict::TYPE_NAME,
|
||||
Self::Func(_) => Func::TYPE_NAME,
|
||||
Self::Args(_) => Args::TYPE_NAME,
|
||||
Self::Module(_) => Module::TYPE_NAME,
|
||||
Self::Dyn(v) => v.type_name(),
|
||||
}
|
||||
}
|
||||
@ -109,6 +112,7 @@ impl Value {
|
||||
Self::Str(v) => item!(text)(v.into()),
|
||||
Self::Content(v) => v,
|
||||
Self::Func(_) => Content::empty(),
|
||||
Self::Module(module) => module.content(),
|
||||
_ => item!(raw)(self.repr().into(), Some("typc".into()), false),
|
||||
}
|
||||
}
|
||||
@ -150,6 +154,7 @@ impl Debug for Value {
|
||||
Self::Dict(v) => Debug::fmt(v, f),
|
||||
Self::Func(v) => Debug::fmt(v, f),
|
||||
Self::Args(v) => Debug::fmt(v, f),
|
||||
Self::Module(v) => Debug::fmt(v, f),
|
||||
Self::Dyn(v) => Debug::fmt(v, f),
|
||||
}
|
||||
}
|
||||
@ -189,6 +194,7 @@ impl Hash for Value {
|
||||
Self::Dict(v) => v.hash(state),
|
||||
Self::Func(v) => v.hash(state),
|
||||
Self::Args(v) => v.hash(state),
|
||||
Self::Module(v) => v.hash(state),
|
||||
Self::Dyn(v) => v.hash(state),
|
||||
}
|
||||
}
|
||||
@ -402,6 +408,7 @@ primitive! { Content: "content",
|
||||
primitive! { Array: "array", Array }
|
||||
primitive! { Dict: "dictionary", Dict }
|
||||
primitive! { Func: "function", Func }
|
||||
primitive! { Module: "module", Module }
|
||||
primitive! { Args: "arguments", Args }
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1430,29 +1430,26 @@ impl ForPattern {
|
||||
}
|
||||
|
||||
node! {
|
||||
/// A module import: `import a, b, c from "utils.typ"`.
|
||||
/// A module import: `import "utils.typ": a, b, c`.
|
||||
ModuleImport
|
||||
}
|
||||
|
||||
impl ModuleImport {
|
||||
/// The items to be imported.
|
||||
pub fn imports(&self) -> Imports {
|
||||
self.0
|
||||
.children()
|
||||
.find_map(|node| match node.kind() {
|
||||
SyntaxKind::Star => Some(Imports::Wildcard),
|
||||
SyntaxKind::ImportItems => {
|
||||
let items = node.children().filter_map(SyntaxNode::cast).collect();
|
||||
Some(Imports::Items(items))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.expect("module import is missing items")
|
||||
/// The module or path from which the items should be imported.
|
||||
pub fn source(&self) -> Expr {
|
||||
self.0.cast_last_child().expect("module import is missing source")
|
||||
}
|
||||
|
||||
/// The path to the file that should be imported.
|
||||
pub fn path(&self) -> Expr {
|
||||
self.0.cast_last_child().expect("module import is missing path")
|
||||
/// The items to be imported.
|
||||
pub fn imports(&self) -> Option<Imports> {
|
||||
self.0.children().find_map(|node| match node.kind() {
|
||||
SyntaxKind::Star => Some(Imports::Wildcard),
|
||||
SyntaxKind::ImportItems => {
|
||||
let items = node.children().filter_map(SyntaxNode::cast).collect();
|
||||
Some(Imports::Items(items))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1471,8 +1468,8 @@ node! {
|
||||
}
|
||||
|
||||
impl ModuleInclude {
|
||||
/// The path to the file that should be included.
|
||||
pub fn path(&self) -> Expr {
|
||||
/// The module or path from which the content should be included.
|
||||
pub fn source(&self) -> Expr {
|
||||
self.0.cast_last_child().expect("module include is missing path")
|
||||
}
|
||||
}
|
||||
|
@ -125,8 +125,8 @@ pub enum SyntaxKind {
|
||||
Import,
|
||||
/// The `include` keyword.
|
||||
Include,
|
||||
/// The `from` keyword.
|
||||
From,
|
||||
/// The `as` keyword.
|
||||
As,
|
||||
|
||||
/// Markup of which all lines must have a minimal indentation.
|
||||
///
|
||||
@ -387,7 +387,7 @@ impl SyntaxKind {
|
||||
Self::Return => "keyword `return`",
|
||||
Self::Import => "keyword `import`",
|
||||
Self::Include => "keyword `include`",
|
||||
Self::From => "keyword `from`",
|
||||
Self::As => "keyword `as`",
|
||||
Self::Markup { .. } => "markup",
|
||||
Self::Text(_) => "text",
|
||||
Self::Linebreak => "linebreak",
|
||||
@ -514,7 +514,7 @@ impl Hash for SyntaxKind {
|
||||
Self::Return => {}
|
||||
Self::Import => {}
|
||||
Self::Include => {}
|
||||
Self::From => {}
|
||||
Self::As => {}
|
||||
Self::Markup { min_indent } => min_indent.hash(state),
|
||||
Self::Text(s) => s.hash(state),
|
||||
Self::Linebreak => {}
|
||||
|
@ -237,7 +237,7 @@ impl<'s> Parser<'s> {
|
||||
self.tokens.set_mode(match kind {
|
||||
Group::Bracket | Group::Strong | Group::Emph => TokenMode::Markup,
|
||||
Group::Math | Group::MathRow(_, _) => TokenMode::Math,
|
||||
Group::Brace | Group::Paren | Group::Expr | Group::Imports => TokenMode::Code,
|
||||
Group::Brace | Group::Paren | Group::Expr => TokenMode::Code,
|
||||
});
|
||||
|
||||
match kind {
|
||||
@ -249,7 +249,6 @@ impl<'s> Parser<'s> {
|
||||
Group::Math => self.assert(SyntaxKind::Dollar),
|
||||
Group::MathRow(l, _) => self.assert(SyntaxKind::Atom(l.into())),
|
||||
Group::Expr => self.repeek(),
|
||||
Group::Imports => self.repeek(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +273,6 @@ impl<'s> Parser<'s> {
|
||||
Group::Math => Some((SyntaxKind::Dollar, true)),
|
||||
Group::MathRow(_, r) => Some((SyntaxKind::Atom(r.into()), true)),
|
||||
Group::Expr => Some((SyntaxKind::Semicolon, false)),
|
||||
Group::Imports => None,
|
||||
} {
|
||||
if self.current.as_ref() == Some(&end) {
|
||||
// If another group closes after a group with the missing
|
||||
@ -346,7 +344,6 @@ impl<'s> Parser<'s> {
|
||||
.next()
|
||||
.map_or(false, |group| group.kind == Group::Math),
|
||||
Some(SyntaxKind::Semicolon) => self.inside(Group::Expr),
|
||||
Some(SyntaxKind::From) => self.inside(Group::Imports),
|
||||
Some(SyntaxKind::Atom(s)) => match s.as_str() {
|
||||
")" => self.inside(Group::MathRow('(', ')')),
|
||||
"}" => self.inside(Group::MathRow('{', '}')),
|
||||
@ -377,7 +374,6 @@ impl<'s> Parser<'s> {
|
||||
|
||||
match self.groups.last().map(|group| group.kind) {
|
||||
Some(Group::Strong | Group::Emph) => n >= 2,
|
||||
Some(Group::Imports) => n >= 1,
|
||||
Some(Group::Expr) if n >= 1 => {
|
||||
// Allow else and method call to continue on next line.
|
||||
self.groups.iter().nth_back(1).map(|group| group.kind)
|
||||
@ -541,8 +537,6 @@ pub enum Group {
|
||||
MathRow(char, char),
|
||||
/// A group ended by a semicolon or a line break: `;`, `\n`.
|
||||
Expr,
|
||||
/// A group for import items, ended by a semicolon, line break or `from`.
|
||||
Imports,
|
||||
}
|
||||
|
||||
impl Group {
|
||||
|
@ -1052,27 +1052,26 @@ fn for_pattern(p: &mut Parser) -> ParseResult {
|
||||
fn module_import(p: &mut Parser) -> ParseResult {
|
||||
p.perform(SyntaxKind::ModuleImport, |p| {
|
||||
p.assert(SyntaxKind::Import);
|
||||
expr(p)?;
|
||||
|
||||
if !p.eat_if(SyntaxKind::Star) {
|
||||
// This is the list of identifiers scenario.
|
||||
p.perform(SyntaxKind::ImportItems, |p| {
|
||||
p.start_group(Group::Imports);
|
||||
let marker = p.marker();
|
||||
let items = collection(p, false).1;
|
||||
if items == 0 {
|
||||
p.expected("import items");
|
||||
}
|
||||
p.end_group();
|
||||
if !p.eat_if(SyntaxKind::Colon) || p.eat_if(SyntaxKind::Star) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
marker.filter_children(p, |n| match n.kind() {
|
||||
SyntaxKind::Ident(_) | SyntaxKind::Comma => Ok(()),
|
||||
_ => Err("expected identifier"),
|
||||
});
|
||||
// This is the list of identifiers scenario.
|
||||
p.perform(SyntaxKind::ImportItems, |p| {
|
||||
let marker = p.marker();
|
||||
let items = collection(p, false).1;
|
||||
if items == 0 {
|
||||
p.expected("import items");
|
||||
}
|
||||
marker.filter_children(p, |n| match n.kind() {
|
||||
SyntaxKind::Ident(_) | SyntaxKind::Comma => Ok(()),
|
||||
_ => Err("expected identifier"),
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
p.expect(SyntaxKind::From)?;
|
||||
expr(p)
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -684,7 +684,7 @@ fn keyword(ident: &str) -> Option<SyntaxKind> {
|
||||
"return" => SyntaxKind::Return,
|
||||
"import" => SyntaxKind::Import,
|
||||
"include" => SyntaxKind::Include,
|
||||
"from" => SyntaxKind::From,
|
||||
"as" => SyntaxKind::As,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.0 KiB |
@ -72,7 +72,8 @@ fn bench_typeset(iai: &mut Iai) {
|
||||
let world = BenchWorld::new();
|
||||
let route = typst::model::Route::default();
|
||||
let module = typst::model::eval(world.track(), route.track(), &world.source).unwrap();
|
||||
iai.run(|| typst::model::typeset(world.track(), &module.content));
|
||||
let content = module.content();
|
||||
iai.run(|| typst::model::typeset(world.track(), &content));
|
||||
}
|
||||
|
||||
fn bench_compile(iai: &mut Iai) {
|
||||
|
@ -432,7 +432,7 @@ fn test_part(
|
||||
let world = (world as &dyn World).track();
|
||||
let route = typst::model::Route::default();
|
||||
let module = typst::model::eval(world, route.track(), source).unwrap();
|
||||
println!("Model:\n{:#?}\n", module.content);
|
||||
println!("Model:\n{:#?}\n", module.content());
|
||||
}
|
||||
|
||||
let (mut frames, errors) = match typst::compile(world, source) {
|
||||
|
@ -79,7 +79,7 @@
|
||||
---
|
||||
// Double block creates a scope.
|
||||
{{
|
||||
import b from "module.typ"
|
||||
import "module.typ": b
|
||||
test(b, 1)
|
||||
}}
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
||||
{
|
||||
let b = "module.typ"
|
||||
let f() = {
|
||||
import b from b
|
||||
import b: b
|
||||
b
|
||||
}
|
||||
test(f(), 1)
|
||||
|
@ -31,7 +31,7 @@
|
||||
{false.ok}
|
||||
|
||||
---
|
||||
// Error: 29-32 unknown field "fun"
|
||||
// Error: 29-32 unknown field `fun`
|
||||
#show heading: node => node.fun
|
||||
= A
|
||||
|
||||
|
@ -1,116 +1,118 @@
|
||||
// Test module imports.
|
||||
// Ref: false
|
||||
|
||||
---
|
||||
// Test importing semantics.
|
||||
|
||||
// A named import.
|
||||
#import item from "module.typ"
|
||||
#test(item(1, 2), 3)
|
||||
// Test basic syntax and semantics.
|
||||
// Ref: true
|
||||
|
||||
// Test that this will be overwritten.
|
||||
#let value = [foo]
|
||||
|
||||
// Import multiple things.
|
||||
#import fn, value from "module.typ"
|
||||
#import "module.typ": fn, value
|
||||
#fn[Like and Subscribe!]
|
||||
#value
|
||||
|
||||
// Should output `bye`.
|
||||
// Stop at semicolon.
|
||||
#import "module.typ": a, c;bye
|
||||
|
||||
---
|
||||
// An item import.
|
||||
#import "module.typ": item
|
||||
#test(item(1, 2), 3)
|
||||
|
||||
// Code mode
|
||||
{
|
||||
import b from "module.typ"
|
||||
import "module.typ": b
|
||||
test(b, 1)
|
||||
}
|
||||
|
||||
// A wildcard import.
|
||||
#import * from "module.typ"
|
||||
#import "module.typ": *
|
||||
|
||||
// It exists now!
|
||||
#d
|
||||
#test(d, 3)
|
||||
|
||||
---
|
||||
// A module import without items.
|
||||
#import "module.typ"
|
||||
#test(module.b, 1)
|
||||
#test((module.item)(1, 2), 3)
|
||||
|
||||
---
|
||||
// Who needs whitespace anyways?
|
||||
#import*from"module.typ"
|
||||
|
||||
// Should output `bye`.
|
||||
// Stop at semicolon.
|
||||
#import a, c from "module.typ";bye
|
||||
#import"module.typ":*
|
||||
|
||||
// Allow the trailing comma.
|
||||
#import a, c, from "module.typ"
|
||||
#import "module.typ": a, c,
|
||||
|
||||
---
|
||||
// Error: 19-21 failed to load file (is a directory)
|
||||
#import name from ""
|
||||
// Error: 9-11 failed to load file (is a directory)
|
||||
#import "": name
|
||||
|
||||
---
|
||||
// Error: 16-27 file not found (searched at typ/compiler/lib/0.2.1)
|
||||
#import * from "lib/0.2.1"
|
||||
// Error: 9-20 file not found (searched at typ/compiler/lib/0.2.1)
|
||||
#import "lib/0.2.1"
|
||||
|
||||
---
|
||||
// Some non-text stuff.
|
||||
// Error: 16-37 file is not valid utf-8
|
||||
#import * from "../../res/rhino.png"
|
||||
// Error: 9-30 file is not valid utf-8
|
||||
#import "../../res/rhino.png"
|
||||
|
||||
---
|
||||
// Unresolved import.
|
||||
// Error: 9-21 unresolved import
|
||||
#import non_existing from "module.typ"
|
||||
// Error: 23-35 unresolved import
|
||||
#import "module.typ": non_existing
|
||||
|
||||
---
|
||||
// Cyclic import of this very file.
|
||||
// Error: 16-30 cyclic import
|
||||
#import * from "./import.typ"
|
||||
// Error: 9-23 cyclic import
|
||||
#import "./import.typ"
|
||||
|
||||
---
|
||||
// Cyclic import in other file.
|
||||
#import * from "./modules/cycle1.typ"
|
||||
#import "./modules/cycle1.typ": *
|
||||
|
||||
This is never reached.
|
||||
|
||||
---
|
||||
// Error: 8 expected import items
|
||||
// Error: 8 expected keyword `from`
|
||||
// Error: 8 expected expression
|
||||
#import
|
||||
|
||||
// Error: 9-19 expected identifier, found string
|
||||
// Error: 19 expected keyword `from`
|
||||
#import "file.typ"
|
||||
|
||||
// Error: 16-19 expected identifier, found string
|
||||
// Error: 22 expected keyword `from`
|
||||
#import afrom, "b", c
|
||||
|
||||
// Error: 9 expected import items
|
||||
#import from "module.typ"
|
||||
|
||||
// Error: 9-10 expected expression, found assignment operator
|
||||
// Error: 10 expected import items
|
||||
#import = from "module.typ"
|
||||
|
||||
// Error: 15 expected expression
|
||||
#import * from
|
||||
|
||||
// An additional trailing comma.
|
||||
// Error: 17-18 expected expression, found comma
|
||||
#import a, b, c,, from "module.typ"
|
||||
|
||||
// Error: 1-6 unexpected keyword `from`
|
||||
#from "module.typ"
|
||||
|
||||
// Error: 2:2 expected semicolon or line break
|
||||
#import * from "module.typ
|
||||
"target
|
||||
|
||||
// Error: 28 expected semicolon or line break
|
||||
#import * from "module.typ" § 0.2.1
|
||||
|
||||
// A star in the list.
|
||||
// Error: 12-13 expected expression, found star
|
||||
#import a, *, b from "module.typ"
|
||||
|
||||
// An item after a star.
|
||||
// Error: 10 expected keyword `from`
|
||||
#import *, a from "module.typ"
|
||||
---
|
||||
// Error: 26-29 expected identifier, found string
|
||||
#import "module.typ": a, "b", c
|
||||
|
||||
---
|
||||
// Error: 9-13 expected identifier, found named pair
|
||||
#import a: 1 from ""
|
||||
// Error: 22 expected import items
|
||||
#import "module.typ":
|
||||
|
||||
---
|
||||
// Error: 23-24 expected expression, found assignment operator
|
||||
// Error: 24 expected import items
|
||||
#import "module.typ": =
|
||||
|
||||
---
|
||||
// An additional trailing comma.
|
||||
// Error: 31-32 expected expression, found comma
|
||||
#import "module.typ": a, b, c,,
|
||||
|
||||
---
|
||||
// Error: 2:2 expected semicolon or line break
|
||||
#import "module.typ
|
||||
"stuff
|
||||
|
||||
---
|
||||
// A star in the list.
|
||||
// Error: 26-27 expected expression, found star
|
||||
#import "module.typ": a, *, b
|
||||
|
||||
---
|
||||
// An item after a star.
|
||||
// Error: 24 expected semicolon or line break
|
||||
#import "module.typ": *, a
|
||||
|
||||
---
|
||||
// Error: 13-17 expected identifier, found named pair
|
||||
#import "": a: 1
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Ref: false
|
||||
|
||||
#import * from "cycle2.typ"
|
||||
#import "cycle2.typ": *
|
||||
#let inaccessible = "wow"
|
||||
|
||||
This is the first element of an import cycle.
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Ref: false
|
||||
|
||||
#import * from "cycle1.typ"
|
||||
#import "cycle1.typ": *
|
||||
#let val = "much cycle"
|
||||
|
||||
This is the second element of an import cycle.
|
||||
|
@ -78,7 +78,7 @@ Another text.
|
||||
= Heading
|
||||
|
||||
---
|
||||
// Error: 25-29 unknown field "page"
|
||||
// Error: 25-29 unknown field `page`
|
||||
#show heading: it => it.page
|
||||
= Heading
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user