Skip to content

Commit

Permalink
Replaced import syntax with use syntax.
Browse files Browse the repository at this point in the history
  • Loading branch information
g-r-a-n-t committed Oct 1, 2021
1 parent a5804e2 commit e073336
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 333 deletions.
112 changes: 42 additions & 70 deletions crates/parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct Module {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum ModuleStmt {
Pragma(Node<Pragma>),
Import(Node<Import>),
Use(Node<Use>),
TypeAlias(Node<TypeAlias>),
Contract(Node<Contract>),
Struct(Node<Struct>),
Expand All @@ -26,17 +26,6 @@ pub struct Pragma {
pub version_requirement: Node<String>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum Import {
Simple {
names: Vec<Node<SimpleImportName>>,
},
From {
path: Node<FromImportPath>,
names: Node<FromImportNames>,
},
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Path {
pub names: Vec<Node<String>>,
Expand Down Expand Up @@ -116,35 +105,6 @@ impl Spanned for GenericArg {
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct SimpleImportName {
pub path: Vec<Node<String>>,
pub alias: Option<Node<String>>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum FromImportPath {
Absolute {
path: Vec<Node<String>>,
},
Relative {
parent_level: usize,
path: Vec<Node<String>>,
},
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum FromImportNames {
Star,
List(Vec<Node<FromImportName>>),
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct FromImportName {
pub name: Node<String>,
pub alias: Option<Node<String>>,
}

/// struct or contract field, with optional 'pub' and 'const' qualifiers
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Field {
Expand Down Expand Up @@ -399,7 +359,7 @@ impl Spanned for ModuleStmt {
fn span(&self) -> Span {
match self {
ModuleStmt::Pragma(inner) => inner.span,
ModuleStmt::Import(inner) => inner.span,
ModuleStmt::Use(inner) => inner.span,
ModuleStmt::TypeAlias(inner) => inner.span,
ModuleStmt::Contract(inner) => inner.span,
ModuleStmt::Struct(inner) => inner.span,
Expand All @@ -426,7 +386,7 @@ impl fmt::Display for ModuleStmt {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ModuleStmt::Pragma(node) => write!(f, "{}", node.kind),
ModuleStmt::Import(node) => write!(f, "{}", node.kind),
ModuleStmt::Use(node) => write!(f, "{}", node.kind),
ModuleStmt::TypeAlias(node) => write!(f, "{}", node.kind),
ModuleStmt::Contract(node) => write!(f, "{}", node.kind),
ModuleStmt::Struct(node) => write!(f, "{}", node.kind),
Expand All @@ -440,9 +400,45 @@ impl fmt::Display for Pragma {
}
}

impl fmt::Display for Import {
fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
todo!()
impl fmt::Display for Use {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "use {}", self.tree.kind)
}
}

impl fmt::Display for UseTree {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
UseTree::Glob { prefix } => write!(f, "{}*", prefix.kind),
UseTree::Simple { path, rename } => {
if let Some(rename) = rename {
write!(f, "{} as {}", path.kind, rename.kind)
} else {
write!(f, "{}", path.kind)
}
}
UseTree::Nested { prefix, children } => {
write!(f, "{}{{{}}}", prefix.kind, node_comma_joined(children))
}
}
}
}

impl fmt::Display for Path {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let joined_names = self
.names
.iter()
.map(|name| format!("{}", name.kind))
.collect::<Vec<_>>()
.join("::");
write!(f, "{}", joined_names)?;

if self.trailing_delim {
write!(f, "::")?;
}

Ok(())
}
}

Expand Down Expand Up @@ -500,30 +496,6 @@ impl fmt::Display for GenericArg {
}
}

impl fmt::Display for SimpleImportName {
fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
todo!()
}
}

impl fmt::Display for FromImportPath {
fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
todo!()
}
}

impl fmt::Display for FromImportNames {
fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
todo!()
}
}

impl fmt::Display for FromImportName {
fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
todo!()
}
}

impl fmt::Display for Field {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.is_pub {
Expand Down
21 changes: 9 additions & 12 deletions crates/parser/src/grammar/Fe.grammar
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,21 @@ file_input: NEWLINE ENDMARKER | module_stmt+ ENDMARKER

module_stmt: import_stmt | type_def | contract_def

########################### import_stmt ##############################
########################### path ######################################

import_stmt: (simple_import | from_import) NEWLINE
path: NAME ('::' NAME)*

simple_import: 'import' (simple_import_name (',' simple_import_name)*)
simple_import_name: dotted_name ['as' NAME]
trailing_delim_path: path '::'

from_import: from_import_parent_alt | from_import_sub_alt
########################### use_stmt ##################################

from_import_parent_alt: 'from' '.'+ 'import' from_import_names
from_import_sub_alt: 'from' from_import_sub_path 'import' from_import_names
use_stmt: 'use' use_tree NEWLINE

from_import_sub_path: '.'* dotted_name
from_import_names: '*' | '(' from_import_names_list ')' | from_import_names_list
from_import_names_list: from_import_name (',' from_import_name)* [',']
from_import_name: NAME ['as' NAME]
use_tree: (simple_use_tree | glob_use_tree | nested_use_tree)

dotted_name: NAME ('.' NAME)*
simple_use_tree: path ['as' NAME]
glob_use_tree: trailing_delim_path '*'
nested_use_tree: trailing_delim_path '{' [use_tree (',' use_tree)* [',']] '}'

########################### type_def #################################

Expand Down
82 changes: 9 additions & 73 deletions crates/parser/src/grammar/module.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::contracts::parse_contract_def;
use super::types::{parse_struct_def, parse_type_alias};
use crate::ast::{Import, Module, ModuleStmt, Path, Pragma, SimpleImportName, Use, UseTree};
use crate::ast::{Module, ModuleStmt, Path, Pragma, Use, UseTree};
use crate::node::{Node, Span};
use crate::{Label, ParseFailed, ParseResult, Parser, TokenKind};

Expand Down Expand Up @@ -31,7 +31,7 @@ pub fn parse_module(par: &mut Parser) -> ParseResult<Node<Module>> {
pub fn parse_module_stmt(par: &mut Parser) -> ParseResult<ModuleStmt> {
let stmt = match par.peek_or_err()? {
TokenKind::Pragma => ModuleStmt::Pragma(parse_pragma(par)?),
TokenKind::Import => ModuleStmt::Import(parse_simple_import(par)?),
TokenKind::Use => ModuleStmt::Use(parse_use(par)?),
TokenKind::Contract => ModuleStmt::Contract(parse_contract_def(par)?),
TokenKind::Struct => ModuleStmt::Struct(parse_struct_def(par)?),
TokenKind::Type => ModuleStmt::TypeAlias(parse_type_alias(par)?),
Expand All @@ -52,68 +52,16 @@ pub fn parse_module_stmt(par: &mut Parser) -> ParseResult<ModuleStmt> {
Ok(stmt)
}

/// Parse an `import` statement. This does not yet support paths, just module
/// names. Note that `from x import y` style imports are handled in
/// [`parse_from_import`].
/// Parse a `use` statement.
/// # Panics
/// Panics if the next token isn't `import`.
pub fn parse_simple_import(par: &mut Parser) -> ParseResult<Node<Import>> {
let import_tok = par.assert(TokenKind::Import);

// TODO: only handles `import foo, bar as baz`

let mut names = vec![];
loop {
let name =
par.expect_with_notes(TokenKind::Name, "failed to parse import statement", |_| {
vec![
"Note: `import` must be followed by a module name or path".into(),
"Example: `import mymodule".into(),
]
})?;

let alias = if par.peek() == Some(TokenKind::As) {
par.next()?;
let tok = par.expect(TokenKind::Name, "failed to parse import statement")?;
Some(tok.into())
} else {
None
};

let span = name.span + alias.as_ref();
names.push(Node::new(
SimpleImportName {
path: vec![Node::new(name.text.to_string(), name.span)],
alias,
},
span,
));

match par.peek() {
Some(TokenKind::Comma) => {
par.next()?;
continue;
}
Some(TokenKind::Newline) | None => break,
Some(_) => {
let tok = par.next()?;
par.unexpected_token_error(tok.span, "failed to parse `import` statement", vec![]);
return Err(ParseFailed);
}
}
}
/// Panics if the next token isn't `use`.
pub fn parse_use(par: &mut Parser) -> ParseResult<Node<Use>> {
let use_tok = par.assert(TokenKind::Use);

let span = import_tok.span + names.last();
Ok(Node::new(Import::Simple { names }, span))
}
let tree = parse_use_tree(par)?;
let tree_span = tree.span;

/// Parse a `from x import y` style import statement.
/// # Panics
/// Always panics. Unimplemented.
pub fn parse_from_import(par: &mut Parser) -> ParseResult<Node<Import>> {
let tok = par.assert(TokenKind::Name);
assert_eq!(tok.text, "from");
todo!("parse from .. import (not supported in rest of compiler yet)")
Ok(Node::new(Use { tree }, use_tok.span + tree_span))
}

/// Parse a `::` delimited path.
Expand Down Expand Up @@ -164,18 +112,6 @@ pub fn parse_path(par: &mut Parser) -> ParseResult<Node<Path>> {
}
}

/// Parse a `use` statement.
/// # Panics
/// Panics if the next token isn't `use`.
pub fn parse_use(par: &mut Parser) -> ParseResult<Node<Use>> {
let use_tok = par.assert(TokenKind::Use);

let tree = parse_use_tree(par)?;
let tree_span = tree.span;

Ok(Node::new(Use { tree }, use_tok.span + tree_span))
}

/// Parse a `use` tree.
pub fn parse_use_tree(par: &mut Parser) -> ParseResult<Node<UseTree>> {
let path = parse_path(par)?;
Expand Down
2 changes: 1 addition & 1 deletion crates/parser/tests/cases/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ test_parse_err! { for_no_in, functions::parse_stmt, true, "for x:\n pass" }
test_parse_err! { fn_no_args, |par| functions::parse_fn_def(par, None), false, "fn f:\n return 5" }
test_parse_err! { fn_def_kw, contracts::parse_contract_def, true, "contract C:\n pub def f(x: u8):\n return x" }
test_parse_err! { if_no_body, functions::parse_stmt, true, "if x:\nelse:\n x" }
test_parse_err! { import_bad_name, module::parse_simple_import, true, "import x as 123" }
test_parse_err! { use_bad_name, module::parse_use, true, "use x as 123" }
test_parse_err! { module_bad_stmt, module::parse_module, true, "if x:\n y" }
test_parse_err! { module_nonsense, module::parse_module, true, "))" }
test_parse_err! { struct_bad_field_name, types::parse_struct_def, true, "struct f:\n pub fn" }
Expand Down
6 changes: 4 additions & 2 deletions crates/parser/tests/cases/parse_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ test_parse! { pragma1, module::parse_pragma, "pragma 0.1.0" }
test_parse! { pragma2, module::parse_pragma, "pragma 0.1.0-alpha" }
test_parse! { pragma3, module::parse_pragma, "pragma >= 1.2, < 1.5" }

test_parse! { import_simple, module::parse_simple_import, "import foo as bar, baz, bing as bop" }
test_parse! { use_simple1, module::parse_use, "use foo::bar" }
test_parse! { use_simple2, module::parse_use, "use foo::bar as baz" }
test_parse! { use_glob, module::parse_use, "use foo::bar::*" }
Expand Down Expand Up @@ -177,7 +176,10 @@ test_parse! { empty_contract_def, contracts::parse_contract_def, r#"contract Foo
test_parse! { module_stmts, module::parse_module, r#"
pragma 0.5.0
import foo as bar, baz as bum
use foo::bar::{
bing as bong,
food::*
}
type X = Map<u8, u16>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
source: crates/parser/tests/cases/errors.rs
expression: "err_string(stringify!(use_bad_name), module::parse_use, true, \"use x as 123\")"

---
error: failed to parse `use` tree
┌─ use_bad_name:1:10
1use x as 123
^^^ Unexpected token. Expected a name


Loading

0 comments on commit e073336

Please sign in to comment.