Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed May 2, 2022
1 parent 4aa56e2 commit 683af78
Show file tree
Hide file tree
Showing 17 changed files with 408 additions and 9 deletions.
1 change: 1 addition & 0 deletions crates/analyzer/src/db/queries/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
}))))
}
ast::ModuleStmt::Pragma(_) => None,
ast::ModuleStmt::Trait(_) => None,
ast::ModuleStmt::Use(_) => None,
ast::ModuleStmt::Event(node) => Some(Item::Event(db.intern_event(Rc::new(Event {
ast: node.clone(),
Expand Down
2 changes: 2 additions & 0 deletions crates/lowering/src/mappers/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn func_def(context: &mut ModuleContext, function: FunctionId) -> Node<fe::F
unsafe_,
name,
args,
generic_params,
return_type: return_type_node,
body,
} = &node.kind;
Expand Down Expand Up @@ -113,6 +114,7 @@ pub fn func_def(context: &mut ModuleContext, function: FunctionId) -> Node<fe::F
unsafe_: *unsafe_,
name: name.clone(),
args,
generic_params: generic_params.clone(),
return_type: Some(lowered_return_type),
body: lowered_body,
};
Expand Down
1 change: 1 addition & 0 deletions crates/lowering/src/mappers/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ fn list_expr_to_fn_def(array: &Array) -> ast::Function {
unsafe_: None,
name: names::list_expr_generator_fn_name(array).into_node(),
args,
generic_params: Vec::new().into_node(),
return_type,
body: [vec![var_decl], assignments, vec![return_stmt]].concat(),
}
Expand Down
36 changes: 36 additions & 0 deletions crates/parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum ModuleStmt {
Contract(Node<Contract>),
Constant(Node<ConstantDecl>),
Struct(Node<Struct>),
Trait(Node<Trait>),
Function(Node<Function>),
Event(Node<Event>),
ParseError(Span),
Expand Down Expand Up @@ -87,6 +88,12 @@ pub struct Struct {
pub pub_qual: Option<Span>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Trait {
pub name: Node<SmolStr>,
pub pub_qual: Option<Span>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum TypeDesc {
Unit,
Expand Down Expand Up @@ -123,6 +130,24 @@ impl Spanned for GenericArg {
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum GenericParameter {
Unbounded(Node<SmolStr>),
Bounded {
name: Node<SmolStr>,
bound: Node<SmolStr>,
},
}

impl Spanned for GenericParameter {
fn span(&self) -> Span {
match self {
GenericParameter::Unbounded(node) => node.span,
GenericParameter::Bounded { name, bound } => name.span + bound.span,
}
}
}

/// 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 @@ -153,6 +178,7 @@ pub struct Function {
pub pub_: Option<Span>,
pub unsafe_: Option<Span>,
pub name: Node<SmolStr>,
pub generic_params: Node<Vec<GenericParameter>>,
pub args: Vec<Node<FunctionArg>>,
pub return_type: Option<Node<TypeDesc>>,
pub body: Vec<Node<FuncStmt>>,
Expand Down Expand Up @@ -406,6 +432,7 @@ impl Spanned for ModuleStmt {
match self {
ModuleStmt::Pragma(inner) => inner.span,
ModuleStmt::Use(inner) => inner.span,
ModuleStmt::Trait(inner) => inner.span,
ModuleStmt::TypeAlias(inner) => inner.span,
ModuleStmt::Contract(inner) => inner.span,
ModuleStmt::Constant(inner) => inner.span,
Expand Down Expand Up @@ -437,6 +464,7 @@ impl fmt::Display for ModuleStmt {
match self {
ModuleStmt::Pragma(node) => write!(f, "{}", node.kind),
ModuleStmt::Use(node) => write!(f, "{}", node.kind),
ModuleStmt::Trait(node) => write!(f, "{}", node.kind),
ModuleStmt::TypeAlias(node) => write!(f, "{}", node.kind),
ModuleStmt::Contract(node) => write!(f, "{}", node.kind),
ModuleStmt::Constant(node) => write!(f, "{}", node.kind),
Expand Down Expand Up @@ -504,6 +532,14 @@ impl fmt::Display for ConstantDecl {
}
}

impl fmt::Display for Trait {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
writeln!(f, "trait {}:", self.name.kind)?;

Ok(())
}
}

impl fmt::Display for TypeAlias {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "type {} = {}", self.name.kind, self.typ.kind)
Expand Down
1 change: 1 addition & 0 deletions crates/parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ pub mod contracts;
pub mod expressions;
pub mod functions;
pub mod module;
pub mod traits;
pub mod types;
97 changes: 93 additions & 4 deletions crates/parser/src/grammar/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use super::expressions::{parse_call_args, parse_expr};
use super::types::parse_type_desc;

use crate::ast::{
BinOperator, Expr, FuncStmt, Function, FunctionArg, RegularFunctionArg, VarDeclTarget,
BinOperator, Expr, FuncStmt, Function, FunctionArg, GenericParameter, RegularFunctionArg,
VarDeclTarget,
};
use crate::lexer::TokenKind;
use crate::node::{Node, Span};
use crate::{Label, ParseFailed, ParseResult, Parser};
use crate::{Label, ParseFailed, ParseResult, Parser, TokenKind};

/// Parse a function definition. The optional `pub` qualifier must be parsed by
/// the caller, and passed in. Next token must be `unsafe` or `fn`.
Expand All @@ -28,7 +28,14 @@ pub fn parse_fn_def(par: &mut Parser, mut pub_qual: Option<Span>) -> ParseResult
}
let fn_tok = par.expect(TokenKind::Fn, "failed to parse function definition")?;
let name = par.expect(TokenKind::Name, "failed to parse function definition")?;
let mut span = fn_tok.span + unsafe_qual + pub_qual + name.span;

let generic_params = if par.peek() == Some(TokenKind::Lt) {
parse_generic_params(par)?
} else {
Node::new(vec![], name.span)
};

let mut span = fn_tok.span + unsafe_qual + pub_qual + name.span + generic_params.span;

let args = match par.peek_or_err()? {
TokenKind::ParenOpen => {
Expand Down Expand Up @@ -87,13 +94,95 @@ pub fn parse_fn_def(par: &mut Parser, mut pub_qual: Option<Span>) -> ParseResult
unsafe_: unsafe_qual,
name: name.into(),
args,
generic_params,
return_type,
body,
},
span,
))
}

/// Parse a single generic function parameter (eg. `T:SomeTrait` in `fn foo<T: SomeTrait>(some_arg: u256) -> bool`).
/// # Panics
/// Panics if the first token isn't `Name`.
pub fn parse_generic_param(par: &mut Parser) -> ParseResult<GenericParameter> {
use TokenKind::*;

let name = par.assert(Name);
match par.optional(Colon) {
Some(_) => {
let bound = par.assert(Name);
return Ok(GenericParameter::Bounded {
name: Node::new(name.text.into(), name.span),
bound: Node::new(bound.text.into(), bound.span),
});
}
None => {
return Ok(GenericParameter::Unbounded(Node::new(
name.text.into(),
name.span,
)))
}
}
}

/// Parse an angle-bracket-wrapped list of generic arguments (eg. `<T, R: SomeTrait>` in `fn foo<T, R: SomeTrait>(some_arg: u256) -> bool`).
/// # Panics
/// Panics if the first token isn't `<`.
pub fn parse_generic_params(par: &mut Parser) -> ParseResult<Node<Vec<GenericParameter>>> {
use TokenKind::*;
let mut span = par.assert(Lt).span;

let mut args = vec![];

let expect_end = |par: &mut Parser| {
// If there's no comma, the next token must be `>`
match par.peek_or_err()? {
Gt => Ok(par.next()?.span),
_ => {
let tok = par.next()?;
par.unexpected_token_error(
tok.span,
"Unexpected token while parsing generic arg list",
vec![],
);
Err(ParseFailed)
}
}
};

loop {
match par.peek_or_err()? {
Gt => {
span += par.next()?.span;
break;
}
Name => {
let typ = parse_generic_param(par)?;
args.push(typ);
if par.peek() == Some(Comma) {
par.next()?;
} else {
span += expect_end(par)?;
break;
}
}

// Invalid generic argument.
_ => {
let tok = par.next()?;
par.unexpected_token_error(
tok.span,
"failed to parse list of generic function parameters",
vec![],
);
return Err(ParseFailed);
}
}
}
Ok(Node::new(args, span))
}

fn parse_fn_param_list(par: &mut Parser) -> ParseResult<Node<Vec<Node<FunctionArg>>>> {
let mut span = par.assert(TokenKind::ParenOpen).span;
let mut params = vec![];
Expand Down
2 changes: 2 additions & 0 deletions crates/parser/src/grammar/module.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::contracts::parse_contract_def;
use super::expressions::parse_expr;
use super::functions::parse_fn_def;
use super::traits::parse_trait_def;
use super::types::{
parse_event_def, parse_path_tail, parse_struct_def, parse_type_alias, parse_type_desc,
};
Expand Down Expand Up @@ -44,6 +45,7 @@ pub fn parse_module_stmt(par: &mut Parser) -> ParseResult<ModuleStmt> {
TokenKind::Use => ModuleStmt::Use(parse_use(par)?),
TokenKind::Contract => ModuleStmt::Contract(parse_contract_def(par, None)?),
TokenKind::Struct => ModuleStmt::Struct(parse_struct_def(par, None)?),
TokenKind::Trait => ModuleStmt::Trait(parse_trait_def(par, None)?),
TokenKind::Type => ModuleStmt::TypeAlias(parse_type_alias(par, None)?),
TokenKind::Const => ModuleStmt::Constant(parse_constant(par, None)?),

Expand Down
55 changes: 55 additions & 0 deletions crates/parser/src/grammar/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use crate::ast::Trait;
use crate::grammar::functions::parse_single_word_stmt;
use crate::node::{Node, Span};
use crate::{ParseFailed, ParseResult, Parser, TokenKind};

/// Parse a trait definition.
/// # Panics
/// Panics if the next token isn't `trait`.
pub fn parse_trait_def(par: &mut Parser, trait_pub_qual: Option<Span>) -> ParseResult<Node<Trait>> {
let trait_tok = par.assert(TokenKind::Trait);

// trait Event:
// pass
//

let trait_name = par.expect_with_notes(
TokenKind::Name,
"failed to parse trait definition",
|_| vec!["Note: `trait` must be followed by a name, which must start with a letter and contain only letters, numbers, or underscores".into()],
)?;

let header_span = trait_tok.span + trait_name.span;
par.enter_block(header_span, "trait definition")?;

loop {
match par.peek() {
Some(TokenKind::Pass) => {
parse_single_word_stmt(par)?;
}
Some(TokenKind::Dedent) => {
par.next()?;
break;
}
None => break,
Some(_) => {
let tok = par.next()?;
par.unexpected_token_error(
tok.span,
"failed to parse trait definition body",
vec![],
);
return Err(ParseFailed);
}
};
}

let span = header_span + trait_pub_qual;
Ok(Node::new(
Trait {
name: Node::new(trait_name.text.into(), trait_name.span),
pub_qual: trait_pub_qual,
},
span,
))
}
3 changes: 3 additions & 0 deletions crates/parser/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ pub enum TokenKind {
SelfValue,
#[token("struct")]
Struct,
#[token("trait")]
Trait,
#[token("type")]
Type,
#[token("unsafe")]
Expand Down Expand Up @@ -249,6 +251,7 @@ impl TokenKind {
Revert => "keyword `revert`",
SelfValue => "keyword `self`",
Struct => "keyword `struct`",
Trait => "keyword `trait`",
Type => "keyword `type`",
Unsafe => "keyword `unsafe`",
While => "keyword `while`",
Expand Down
2 changes: 2 additions & 0 deletions crates/parser/tests/cases/parse_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ test_parse! { type_tuple, types::parse_type_desc, "(u8, u16, address, Map<u8, u8
test_parse! { type_unit, types::parse_type_desc, "()" }

test_parse! { fn_def, try_parse_module, "fn transfer(from sender: address, to recip: address, _ val: u64) -> bool:\n false"}

test_parse! { fn_def_generic, try_parse_module, "fn foo<T, R: Event>(this: T, that: R, _ val: u64) -> bool:\n false"}
test_parse! { fn_def_pub, try_parse_module, "pub fn foo21(x: bool, y: address,) -> bool:\n x"}
test_parse! { fn_def_unsafe, try_parse_module, "unsafe fn foo21(x: bool, y: address,) -> bool:\n x"}
test_parse! { fn_def_pub_unsafe, try_parse_module, "pub unsafe fn foo21(x: bool, y: address,) -> bool:\n x"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ error: failed to parse module
1if x:
^^ unexpected token
= Note: expected import, contract, struct, type, const or event
= Note: expected import, contract, struct, trait, type, const or event


Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: crates/parser/tests/cases/parse_ast.rs
expression: "ast_string(stringify!(fn_def), try_parse_module,\n \"fn transfer(from sender: address, to recip: address, _ val: u64) -> bool:\\n false\")"
expression: "ast_string(stringify!(fn_def), try_parse_module,\n \"fn transfer(from sender: address, to recip: address, _ val: u64) -> bool:\\n false\")"

---
Node(
Expand All @@ -17,6 +17,13 @@ Node(
end: 11,
),
),
generic_params: Node(
kind: [],
span: Span(
start: 3,
end: 11,
),
),
args: [
Node(
kind: Regular(RegularFunctionArg(
Expand Down
Loading

0 comments on commit 683af78

Please sign in to comment.