Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
12 changes: 10 additions & 2 deletions rust/candid/src/types/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl IDLArgType {
pub struct TypeField {
pub label: Label,
pub typ: IDLType,
pub doc_comment_lines: Vec<String>,
Comment thread
ilbertt marked this conversation as resolved.
Outdated
}

#[derive(Debug)]
Expand All @@ -107,12 +108,19 @@ pub enum Dec {
pub struct Binding {
pub id: String,
pub typ: IDLType,
pub doc_comment_lines: Vec<String>,
}

#[derive(Debug, Default)]
#[derive(Debug)]
pub struct IDLActorType {
pub typ: IDLType,
pub doc_comment_lines: Vec<String>,
}

#[derive(Debug)]
pub struct IDLProg {
pub decs: Vec<Dec>,
pub actor: Option<IDLType>,
pub actor: Option<IDLActorType>,
}

#[derive(Debug)]
Expand Down
36 changes: 21 additions & 15 deletions rust/candid_parser/src/grammar.lalrpop
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use super::test::{Assert, Input, Test};
use super::token::{Token, error, error2, LexicalError, Span};
use super::token::{Token, error, error2, LexicalError, Span, TriviaMap};
use candid::{Principal, types::Label};
use candid::types::syntax::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs, IDLArgType};
use candid::types::syntax::{IDLType, PrimType, TypeField, FuncType, Binding, Dec, IDLProg, IDLTypes, IDLInitArgs, IDLArgType, IDLActorType};
use candid::types::value::{IDLField, IDLValue, IDLArgs, VariantValue};
use candid::types::{TypeEnv, FuncMode};
use candid::utils::check_unique;

grammar;
grammar(trivia: Option<&TriviaMap>);

extern {
type Location = usize;
Expand Down Expand Up @@ -174,7 +174,7 @@ pub Typ: IDLType = {
Label::Unnamed(_) => { id = id + 1; Label::Unnamed(id - 1) },
ref l => { id = l.get_id() + 1; l.clone() },
};
TypeField { label, typ: f.typ.clone() }
TypeField { label, typ: f.typ.clone(), doc_comment_lines: f.doc_comment_lines.clone() }
}).collect();
fs.sort_unstable_by_key(|TypeField { label, .. }| label.get_id());
check_unique(fs.iter().map(|f| &f.label)).map_err(|e| error2(e, span))?;
Expand Down Expand Up @@ -202,19 +202,19 @@ PrimTyp: IDLType = {
}

FieldTyp: TypeField = {
<n:FieldId> ":" <t:Typ> =>? Ok(TypeField { label: Label::Id(n), typ: t }),
<n:Name> ":" <t:Typ> => TypeField { label: Label::Named(n), typ: t },
<doc_comment: DocComment> <id:FieldId> ":" <typ:Typ> =>? Ok(TypeField { label: Label::Id(id), typ, doc_comment_lines: doc_comment.unwrap_or_default() }),
<doc_comment: DocComment> <n:Name> ":" <typ:Typ> => TypeField { label: Label::Named(n), typ, doc_comment_lines: doc_comment.unwrap_or_default() },
}

RecordFieldTyp: TypeField = {
FieldTyp => <>,
Typ => TypeField { label: Label::Unnamed(0), typ: <> },
<doc_comment: DocComment> <typ:Typ> => TypeField { label: Label::Unnamed(0), typ, doc_comment_lines: doc_comment.unwrap_or_default() },
}

VariantFieldTyp: TypeField = {
FieldTyp => <>,
Name => TypeField { label: Label::Named(<>), typ: IDLType::PrimT(PrimType::Null) },
FieldId =>? Ok(TypeField { label: Label::Id(<>), typ: IDLType::PrimT(PrimType::Null) }),
<doc_comment: DocComment> <n:Name> => TypeField { label: Label::Named(n), typ: IDLType::PrimT(PrimType::Null), doc_comment_lines: doc_comment.unwrap_or_default() },
<doc_comment: DocComment> <id:FieldId> =>? Ok(TypeField { label: Label::Id(id), typ: IDLType::PrimT(PrimType::Null), doc_comment_lines: doc_comment.unwrap_or_default() }),
}

ArgTupTyp: Vec<IDLArgType> = "(" <SepBy<ArgTyp, ",">> ")" =>? {
Expand Down Expand Up @@ -253,13 +253,13 @@ ActorTyp: Vec<Binding> = {
}

MethTyp: Binding = {
<n:Name> ":" <f:FuncTyp> => Binding { id: n, typ: IDLType::FuncT(f) },
<n:Name> ":" <id:"id"> => Binding { id: n, typ: IDLType::VarT(id) },
<doc_comment: DocComment> <n:Name> ":" <f:FuncTyp> => Binding { id: n, typ: IDLType::FuncT(f), doc_comment_lines: doc_comment.unwrap_or_default() },
<doc_comment: DocComment> <n:Name> ":" <id:"id"> => Binding { id: n, typ: IDLType::VarT(id), doc_comment_lines: doc_comment.unwrap_or_default() },
}

// Type declarations
Def: Dec = {
"type" <id:"id"> "=" <t:Typ> => Dec::TypD(Binding { id: id, typ: t }),
<doc_comment: DocComment> "type" <id:"id"> "=" <t:Typ> => Dec::TypD(Binding { id: id, typ: t, doc_comment_lines: doc_comment.unwrap_or_default() }),
"import" <Text> => Dec::ImportType(<>),
"import" "service" <Text> => Dec::ImportServ(<>),
}
Expand All @@ -269,9 +269,9 @@ Actor: IDLType = {
"id" => IDLType::VarT(<>),
}

MainActor: IDLType = {
"service" "id"? ":" <t:Actor> ";"? => <>,
"service" "id"? ":" <args:ArgTupTyp> "->" <t:Actor> ";"? => IDLType::ClassT(args, Box::new(t)),
MainActor: IDLActorType = {
<doc_comment: DocComment> "service" "id"? ":" <t:Actor> ";"? => IDLActorType { typ: t, doc_comment_lines: doc_comment.unwrap_or_default() },
<doc_comment: DocComment> "service" "id"? ":" <args:ArgTupTyp> "->" <t:Actor> ";"? => IDLActorType { typ: IDLType::ClassT(args, Box::new(t)), doc_comment_lines: doc_comment.unwrap_or_default() },
}

pub IDLProg: IDLProg = {
Expand Down Expand Up @@ -327,3 +327,9 @@ SepBy<T, S>: Vec<T> = {
#[inline]
Sp<T>: (T, Span) =
<l: @L> <t: T> <r: @R> => (t, l..r);

#[inline]
DocComment: Option<Vec<String>> =
<l: @L> => {
trivia.and_then(|t| t.borrow().get(&l).cloned())
};
16 changes: 9 additions & 7 deletions rust/candid_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,31 +143,33 @@ pub mod random;
pub mod test;

pub fn parse_idl_prog(str: &str) -> Result<candid::types::syntax::IDLProg> {
let lexer = token::Tokenizer::new(str);
Ok(grammar::IDLProgParser::new().parse(lexer)?)
let trivia = token::TriviaMap::default();
let lexer = token::Tokenizer::new_with_trivia(str, trivia.clone());
let res = grammar::IDLProgParser::new().parse(Some(&trivia.clone()), lexer)?;
Ok(res)
}

pub fn parse_idl_init_args(str: &str) -> Result<candid::types::syntax::IDLInitArgs> {
let lexer = token::Tokenizer::new(str);
Ok(grammar::IDLInitArgsParser::new().parse(lexer)?)
Ok(grammar::IDLInitArgsParser::new().parse(None, lexer)?)
}

pub fn parse_idl_type(str: &str) -> Result<candid::types::syntax::IDLType> {
let lexer = token::Tokenizer::new(str);
Ok(grammar::TypParser::new().parse(lexer)?)
Ok(grammar::TypParser::new().parse(None, lexer)?)
}

pub fn parse_idl_types(str: &str) -> Result<candid::types::syntax::IDLTypes> {
let lexer = token::Tokenizer::new(str);
Ok(grammar::TypsParser::new().parse(lexer)?)
Ok(grammar::TypsParser::new().parse(None, lexer)?)
}

pub fn parse_idl_args(s: &str) -> crate::Result<candid::IDLArgs> {
let lexer = token::Tokenizer::new(s);
Ok(grammar::ArgsParser::new().parse(lexer)?)
Ok(grammar::ArgsParser::new().parse(None, lexer)?)
}

pub fn parse_idl_value(s: &str) -> crate::Result<candid::IDLValue> {
let lexer = token::Tokenizer::new(s);
Ok(grammar::ArgParser::new().parse(lexer)?)
Ok(grammar::ArgParser::new().parse(None, lexer)?)
}
2 changes: 1 addition & 1 deletion rust/candid_parser/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl std::str::FromStr for Test {
type Err = Error;
fn from_str(str: &str) -> std::result::Result<Self, Self::Err> {
let lexer = super::token::Tokenizer::new(str);
Ok(super::grammar::TestParser::new().parse(lexer)?)
Ok(super::grammar::TestParser::new().parse(None, lexer)?)
}
}

Expand Down
52 changes: 44 additions & 8 deletions rust/candid_parser/src/token.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::{cell::RefCell, collections::HashMap, mem, rc::Rc};

use lalrpop_util::ParseError;
use logos::{Lexer, Logos};

#[derive(Logos, Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
#[logos(skip r"[ \t\r\n]+")]
#[logos(skip r"//[^\n]*")] // line comment
pub enum Token {
#[regex(r"//[^\n]*")]
DocComment,
Comment thread
ilbertt marked this conversation as resolved.
Outdated
#[token("/*")]
StartComment,
#[token("=")]
Expand Down Expand Up @@ -118,22 +121,40 @@ fn parse_number(lex: &mut Lexer<Token>) -> String {
}
}

fn parse_doc_comment(lex: &Lexer<Token>) -> String {
lex.slice().trim_start_matches("///").trim().to_string()
Comment thread
ilbertt marked this conversation as resolved.
Outdated
}

pub type TriviaMap = Rc<RefCell<HashMap<usize, Vec<String>>>>;

pub struct Tokenizer<'input> {
lex: Lexer<'input, Token>,
comment_buffer: Vec<String>,
trivia: Option<TriviaMap>,
}

impl<'input> Tokenizer<'input> {
pub fn new(input: &'input str) -> Self {
let lex = Token::lexer(input);
Tokenizer { lex }
Tokenizer {
lex,
comment_buffer: vec![],
trivia: None,
}
}

pub fn new_with_trivia(input: &'input str, trivia: TriviaMap) -> Self {
let lex = Token::lexer(input);
Tokenizer {
lex,
comment_buffer: vec![],
trivia: Some(trivia),
}
}
}

pub type Span = std::ops::Range<usize>;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Spanned<T> {
pub span: Span,
pub value: T,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LexicalError {
pub err: String,
Expand Down Expand Up @@ -179,6 +200,13 @@ impl Iterator for Tokenizer<'_> {
let err = format!("Unknown token {}", self.lex.slice());
Some(Err(LexicalError::new(err, span)))
}
Ok(Token::DocComment) => {
let content = parse_doc_comment(&self.lex);
if self.trivia.is_some() {
self.comment_buffer.push(content.to_string());
}
self.next()
}
Ok(Token::StartComment) => {
let mut lex = self.lex.to_owned().morph::<Comment>();
let mut nesting = 1;
Expand Down Expand Up @@ -278,7 +306,15 @@ impl Iterator for Tokenizer<'_> {
self.lex = lex.morph::<Token>();
Some(Ok((span.start, Token::Text(result), self.lex.span().end)))
}
Ok(token) => Some(Ok((span.start, token, span.end))),
Ok(token) => {
if let Some(trivia) = &mut self.trivia {
if !self.comment_buffer.is_empty() {
let content: Vec<String> = mem::take(&mut self.comment_buffer);
trivia.borrow_mut().insert(span.start, content);
}
}
Some(Ok((span.start, token, span.end)))
}
}
}
}
16 changes: 11 additions & 5 deletions rust/candid_parser/src/typing.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::{parse_idl_prog, pretty_parse_idl_prog, Error, Result};
use candid::types::{
syntax::{Binding, Dec, IDLArgType, IDLInitArgs, IDLProg, IDLType, PrimType, TypeField},
syntax::{
Binding, Dec, IDLActorType, IDLArgType, IDLInitArgs, IDLProg, IDLType, PrimType, TypeField,
},
ArgType, Field, Function, Type, TypeEnv, TypeInner,
};
use candid::utils::check_unique;
Expand Down Expand Up @@ -141,7 +143,11 @@ fn check_meths(env: &Env, ms: &[Binding]) -> Result<Vec<(String, Type)>> {
fn check_defs(env: &mut Env, decs: &[Dec]) -> Result<()> {
for dec in decs.iter() {
match dec {
Dec::TypD(Binding { id, typ }) => {
Dec::TypD(Binding {
id,
typ,
doc_comment_lines: _,
}) => {
let t = check_type(env, typ)?;
env.te.0.insert(id.to_string(), t);
}
Expand Down Expand Up @@ -176,7 +182,7 @@ fn check_cycle(env: &TypeEnv) -> Result<()> {

fn check_decs(env: &mut Env, decs: &[Dec]) -> Result<()> {
for dec in decs.iter() {
if let Dec::TypD(Binding { id, typ: _ }) = dec {
if let Dec::TypD(Binding { id, .. }) = dec {
let duplicate = env.te.0.insert(id.to_string(), TypeInner::Unknown.into());
if duplicate.is_some() {
return Err(Error::msg(format!("duplicate binding for {id}")));
Expand All @@ -191,8 +197,8 @@ fn check_decs(env: &mut Env, decs: &[Dec]) -> Result<()> {
Ok(())
}

fn check_actor(env: &Env, actor: &Option<IDLType>) -> Result<Option<Type>> {
match actor {
fn check_actor(env: &Env, actor: &Option<IDLActorType>) -> Result<Option<Type>> {
match actor.as_ref().map(|a| &a.typ) {
None => Ok(None),
Some(IDLType::ClassT(ts, t)) => {
let mut args = Vec::new();
Expand Down