From c8f058bbb0795ecd7b9612f34655ab627e39cc6f Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 12 Mar 2024 22:49:02 +0100 Subject: [PATCH 1/2] Use new parser Signed-off-by: Ryan Levick --- src/command.rs | 150 +++++---- src/command/alt_parser.rs | 674 ++++++++++++++++++++++++++++++++++++++ src/command/tokenizer.rs | 254 ++++++++++++++ src/evaluator.rs | 50 ++- src/runtime.rs | 49 +-- src/wit.rs | 20 +- 6 files changed, 1065 insertions(+), 132 deletions(-) create mode 100644 src/command/alt_parser.rs create mode 100644 src/command/tokenizer.rs diff --git a/src/command.rs b/src/command.rs index 5372a75..067722d 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,46 +1,45 @@ +pub mod alt_parser; pub mod parser; +mod tokenizer; use std::collections::HashMap; -use anyhow::{anyhow, bail, Context as _}; +use anyhow::{bail, Context as _}; use colored::Colorize; use wasmtime::component::Val; +use self::alt_parser::ItemIdent; +use self::tokenizer::TokenKind; + use super::runtime::Runtime; use super::wit::WorldResolver; -use crate::command::parser::ItemIdent; use crate::evaluator::Evaluator; use crate::wit::Expansion; -use parser::SpannedStr; pub enum Cmd<'a> { BuiltIn { - name: SpannedStr<'a>, - args: Vec>, + name: &'a str, + args: Vec>, }, - Eval(parser::Expr<'a>), + Eval(alt_parser::Expr<'a>), Assign { - ident: SpannedStr<'a>, - value: parser::Expr<'a>, + ident: &'a str, + value: alt_parser::Expr<'a>, }, } impl<'a> Cmd<'a> { - pub fn parse(s: &'a str) -> anyhow::Result>> { - let s = s.trim(); - if s.is_empty() { - return Ok(None); - } - - // try to parse a function - let (rest, line) = parser::Line::parse(s).map_err(|e| anyhow!("{e}"))?; - if !rest.is_empty() { - anyhow::bail!("unexpected end of input: '{rest}'"); - } + pub fn parse(input: &'a str) -> anyhow::Result>> { + let tokens = tokenizer::Token::tokenize(input)?; + let line = alt_parser::Line::parse(tokens) + .expect("TODO: using ? here leads to borrow checker errors"); log::debug!("Parsed line: {line:?}"); match line { - parser::Line::Expr(expr) => Ok(Some(Cmd::Eval(expr))), - parser::Line::Assignment(ident, value) => Ok(Some(Cmd::Assign { ident, value })), - parser::Line::Builtin(name, args) => Ok(Some(Cmd::BuiltIn { name, args })), + alt_parser::Line::Expr(expr) => Ok(Some(Cmd::Eval(expr))), + alt_parser::Line::Assignment(ident, value) => Ok(Some(Cmd::Assign { ident, value })), + alt_parser::Line::BuiltIn(builtin) => Ok(Some(Cmd::BuiltIn { + name: builtin.name, + args: builtin.rest, + })), } } @@ -56,11 +55,11 @@ impl<'a> Cmd<'a> { let mut eval = Evaluator::new(runtime, querier, scope); match self { Cmd::Eval(expr) => match expr { - parser::Expr::Literal(l) => { + alt_parser::Expr::Literal(l) => { let val = eval.eval_literal(l, None)?; println!("{}: {}", format_val(&val), val_as_type(&val)); } - parser::Expr::Ident(ident) => match scope.get(&*ident) { + alt_parser::Expr::Ident(ident) => match scope.get(ident) { Some(val) => { println!("{}: {}", format_val(val), val_as_type(val)) } @@ -68,8 +67,8 @@ impl<'a> Cmd<'a> { anyhow::bail!("no identifier '{ident}' in scope") } }, - parser::Expr::FunctionCall(ident, args) => { - let results = eval.call_func(ident, args)?; + alt_parser::Expr::FunctionCall(func) => { + let results = eval.call_func(func.ident, func.args)?; println!( "{}", results @@ -99,16 +98,22 @@ impl<'a> Cmd<'a> { } } } - Cmd::BuiltIn { name, args } if name == "imports" => { + Cmd::BuiltIn { + name: "imports", + args, + } => { let include_wasi = match args.as_slice() { [] => true, - [flag] if *flag == "--no-wasi" => false, - [flag] => { - bail!("unrecorgnized flag for imports builtin '{}'", flag) - } + [t] => match t.token() { + TokenKind::Flag("no-wasi") => false, + TokenKind::Flag(flag) => { + bail!("unrecognized flag for imports builtin '{flag}'") + } + _ => bail!("unrecognized token {}", t.input.str), + }, _ => { bail!( - "wrong number of arguments to imports function. Expected 0 got {}", + "wrong number of arguments to imports function. Expected 1 got {}", args.len() ) } @@ -120,64 +125,69 @@ impl<'a> Cmd<'a> { } } } - Cmd::BuiltIn { name, args } if name == "type" => { - match args.as_slice() { - &[name] => { - let types = querier.types_by_name(&*name); - for (interface, ty) in &types { - let typ = querier.display_wit_type_def(ty, Expansion::Expanded(1)); - let name = &ty.name; - let interface = interface.and_then(|i| querier.interface_name(i)); - let ident = match (interface, name) { - (Some(i), Some(n)) => format!("{i}#{n}: "), - (None, Some(n)) => format!("{n}: "), - _ => todo!(), - }; - println!("{ident}{typ}"); - } - } - _ => bail!( - "wrong number of arguments to inspect function. Expected 1 got {}", - args.len() - ), - }; - } + // Cmd::BuiltIn { name: "type", args } => { + // match args.as_slice() { + // &[name] => { + // let types = querier.types_by_name(&*name); + // for (interface, ty) in &types { + // let typ = querier.display_wit_type_def(ty, Expansion::Expanded(1)); + // let name = &ty.name; + // let interface = interface.and_then(|i| querier.interface_name(i)); + // let ident = match (interface, name) { + // (Some(i), Some(n)) => format!("{i}#{n}: "), + // (None, Some(n)) => format!("{n}: "), + // _ => todo!(), + // }; + // println!("{ident}{typ}"); + // } + // } + // _ => bail!( + // "wrong number of arguments to inspect function. Expected 1 got {}", + // args.len() + // ), + // }; + // } Cmd::BuiltIn { name, args } if name == "compose" => { - let &[path] = args.as_slice() else { + let &[token] = args.as_slice() else { bail!( "wrong number of arguments to compose function. Expected 1 got {}", args.len() ) }; + let TokenKind::String(path) = token.token() else { + bail!("unrecognized token {}", token.input.str); + }; let adapter = std::fs::read(&*path).context("could not read path to adapter module")?; runtime.compose(&adapter)?; *querier = WorldResolver::from_bytes(runtime.component_bytes())?; } Cmd::BuiltIn { name, args } if name == "link" => { - let &[import_ident, export_ident, component] = args.as_slice() else { - bail!("wrong number of arguments. Expected 3 got {}", args.len()) + let mut args = args.into_iter().collect(); + let Ok(Some(import_ident)) = ItemIdent::try_parse(&mut args) else { + bail!("import_ident is not a proper item identifier"); }; - let Ok((_, import_ident)) = ItemIdent::parse((&*import_ident).into()) else { - bail!("'{import_ident}' is not a proper item identifier"); - }; - let Ok((_, export_ident)) = ItemIdent::parse((&*export_ident).into()) else { - bail!("'{export_ident}' is not a proper item identifier"); + let Ok(Some(export_ident)) = ItemIdent::try_parse(&mut args) else { + bail!("export_ident is not a proper item identifier"); }; - let component_bytes = std::fs::read(component.as_str()) + let Some(TokenKind::String(component)) = args.pop_front().map(|t| t.token()) else { + bail!("TODO"); + }; + let component_bytes = std::fs::read(component) .with_context(|| format!("could not read component '{component}'"))?; runtime.stub(&querier, import_ident, export_ident, &component_bytes)?; } - Cmd::BuiltIn { name, args } if name == "inspect" => { - let &[ident] = args.as_slice() else { - bail!("wrong number of arguments. Expected 1 got {}", args.len()) - }; - let Ok((_, ident)) = ItemIdent::parse((&*ident).into()) else { - bail!("'{ident}' is not a proper item identifier"); + Cmd::BuiltIn { + name: "inspect", + args, + } => { + let mut args = args.into_iter().collect(); + let Ok(Some(ident)) = ItemIdent::try_parse(&mut args) else { + bail!("ident is not a proper item identifier"); }; match ident { - ItemIdent::Function(ident) => { + ItemIdent::Item(ident) => { let f = querier .exported_function(ident) .or_else(|| querier.imported_function(ident)); diff --git a/src/command/alt_parser.rs b/src/command/alt_parser.rs new file mode 100644 index 0000000..25a40b0 --- /dev/null +++ b/src/command/alt_parser.rs @@ -0,0 +1,674 @@ +use std::collections::VecDeque; + +use crate::command::tokenizer::TokenKind; + +use super::tokenizer::Token; + +#[derive(Debug, PartialEq)] +pub enum Line<'a> { + Expr(Expr<'a>), + BuiltIn(BuiltIn<'a>), + Assignment(&'a str, Expr<'a>), +} + +impl<'a> Line<'a> { + pub fn parse(mut tokens: VecDeque>) -> Result, ParserError<'a>> { + let result = match BuiltIn::try_parse(&mut tokens)? { + Some(builtin) => Ok(Self::BuiltIn(builtin)), + None => match Self::try_parse_assignment(&mut tokens)? { + Some((ident, expr)) => Ok(Self::Assignment(ident, expr)), + None => match Expr::try_parse(&mut tokens)? { + Some(e) => Ok(Self::Expr(e)), + None => todo!(), + }, + }, + }; + if !tokens.is_empty() { + return Err(ParserError::RemainingInput); + } + result + } + + fn try_parse_assignment( + tokens: &mut VecDeque>, + ) -> Result)>, ParserError<'a>> { + let Some(token) = tokens.front() else { + return Ok(None); + }; + println!("0 {tokens:?}"); + let TokenKind::Ident(ident) = token.token() else { + return Ok(None); + }; + println!("1 {tokens:?}"); + let token = *token; + let _ = tokens.pop_front(); + if matches!(tokens.front().map(|t| t.token()), Some(TokenKind::Equal)) { + let _ = tokens.pop_front(); + println!("{tokens:?}"); + match Expr::try_parse(tokens)? { + Some(e) => Ok(Some((ident, e))), + None => Err(ParserError::ExpectedExpr), + } + } else { + tokens.push_front(token); + Ok(None) + } + } +} + +#[derive(Debug, PartialEq)] +pub struct BuiltIn<'a> { + pub name: &'a str, + pub rest: Vec>, +} + +impl<'a> BuiltIn<'a> { + fn try_parse(tokens: &mut VecDeque>) -> Result>, ParserError<'a>> { + let Some(TokenKind::Builtin(ident)) = tokens.front().map(|t| t.token()) else { + return Ok(None); + }; + tokens.pop_front(); + Ok(Some(BuiltIn { + name: ident, + rest: tokens.drain(..).collect(), + })) + } +} + +#[derive(Debug, PartialEq)] +pub enum ParserError<'a> { + UnexpectedToken(Token<'a>), + UnexpectedEndOfInput, + RemainingInput, + ExpectedExpr, +} + +impl std::error::Error for ParserError<'_> {} + +impl std::fmt::Display for ParserError<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParserError::UnexpectedToken(_) => f.write_str("unexpected token"), + ParserError::UnexpectedEndOfInput => f.write_str("unexpected end of input"), + ParserError::RemainingInput => f.write_str("remaining input"), + ParserError::ExpectedExpr => f.write_str("unexpected expression"), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum Expr<'a> { + FunctionCall(FunctionCall<'a>), + Ident(&'a str), + Literal(Literal<'a>), +} + +impl<'a> Expr<'a> { + fn try_parse(input: &mut VecDeque>) -> Result>, ParserError<'a>> { + let Some(first) = input.front() else { + return Ok(None); + }; + match first.token() { + TokenKind::String(s) => { + input.pop_front(); + Ok(Some(Expr::Literal(Literal::String(s)))) + } + TokenKind::Number(n) => { + input.pop_front(); + Ok(Some(Expr::Literal(Literal::Number(n)))) + } + TokenKind::OpenBracket => { + input.pop_front(); + enum State { + ExpectExpr, + ExpectComma, + } + let mut state = State::ExpectExpr; + let mut items = vec![]; + while let Some(token) = input.front() { + match token.token() { + TokenKind::ClosedBracket => { + input.pop_front(); + return Ok(Some(Expr::Literal(Literal::List(List { items })))); + } + TokenKind::Comma if matches!(state, State::ExpectComma) => { + input.pop_front(); + state = State::ExpectExpr; + } + _ => { + let expr = Expr::try_parse(input)?; + if let Some(expr) = expr { + items.push(expr); + state = State::ExpectComma; + } else { + return Err(ParserError::UnexpectedEndOfInput); + } + } + } + } + return Err(ParserError::UnexpectedEndOfInput); + } + TokenKind::OpenBrace => { + input.pop_front(); + enum State<'a> { + ExpectIdent, + ExpectColon(&'a str), + ExpectExpr(&'a str), + ExpectComma, + } + let mut state = State::ExpectIdent; + let mut fields = vec![]; + while let Some(token) = input.front() { + match (token.token(), state) { + (TokenKind::ClosedBrace, State::ExpectComma | State::ExpectIdent) => { + input.pop_front(); + return Ok(Some(Expr::Literal(Literal::Record(Record { fields })))); + } + (TokenKind::Comma, State::ExpectComma) => { + input.pop_front(); + state = State::ExpectIdent; + } + (TokenKind::Colon, State::ExpectColon(ident)) => { + input.pop_front(); + state = State::ExpectExpr(ident); + } + (_, State::ExpectIdent) => { + let ident = Literal::parse_ident(input)?; + state = State::ExpectColon(ident); + } + (_, State::ExpectExpr(ident)) => { + let expr = Expr::try_parse(input)?; + if let Some(expr) = expr { + fields.push((ident, expr)); + state = State::ExpectComma; + } else { + return Err(ParserError::UnexpectedEndOfInput); + } + } + _ => return Err(ParserError::UnexpectedToken(*token)), + } + } + return Err(ParserError::UnexpectedEndOfInput); + } + TokenKind::Ident(_) => { + let func = FunctionCall::try_parse(input)?; + match func { + Some(f) => Ok(Some(Expr::FunctionCall(f))), + None => Ok(Some(Expr::Ident(Literal::parse_ident(input)?))), + } + } + + _ => return Ok(None), + } + } +} + +#[derive(Debug, PartialEq)] +pub struct FunctionCall<'a> { + pub ident: Ident<'a>, + pub args: Vec>, +} + +impl<'a> FunctionCall<'a> { + fn try_parse(input: &mut VecDeque>) -> Result, ParserError<'a>> { + let original = input.clone(); + let Some(function_ident) = Ident::try_parse(input)? else { + return Ok(None); + }; + let next = input.front(); + if next.map(|t| t.token()) != Some(TokenKind::OpenParen) { + // If we failed to find an open paren then we need to completely bail + // on function parsing which means restoring the input state back to + // its original form. + *input = original; + return Ok(None); + } + expect_token(input, |t| t == TokenKind::OpenParen)?; + let mut args = Vec::new(); + loop { + let Some(expr) = Expr::try_parse(input)? else { + break; + }; + args.push(expr); + if input.front().map(|t| t.token()) != Some(TokenKind::Comma) { + break; + } + } + expect_token(input, |t| t == TokenKind::ClosedParen)?; + Ok(Some(FunctionCall { + ident: function_ident, + args, + })) + } +} + +fn expect_token<'a>( + input: &mut VecDeque>, + pred: impl FnOnce(TokenKind<'a>) -> bool, +) -> Result<(), ParserError<'a>> { + let Some(token) = input.pop_front() else { + return Err(ParserError::UnexpectedEndOfInput); + }; + if !pred(token.token()) { + return Err(ParserError::UnexpectedToken(token)); + } + Ok(()) +} + +// TODO: Rename this to Ident and Ident to ItemIdent +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum ItemIdent<'a> { + Item(Ident<'a>), + Interface(InterfaceIdent<'a>), +} + +impl<'a> ItemIdent<'a> { + pub(crate) fn try_parse( + input: &mut VecDeque>, + ) -> Result>, ParserError<'a>> { + match Ident::try_parse(input)? { + Some(i) => Ok(Some(Self::Item(i))), + None => Ok(InterfaceIdent::try_parse(input)?.map(Self::Interface)), + } + } +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub struct Ident<'a> { + pub interface: Option>, + pub item: &'a str, +} + +impl<'a> Ident<'a> { + fn try_parse(input: &mut VecDeque>) -> Result, ParserError<'a>> { + let interface = InterfaceIdent::try_parse(input)?; + match interface { + Some(i) if i.package.is_none() => { + if input.front().map(|t| t.token()) == Some(TokenKind::Hash) { + input.pop_front(); + let ident = Literal::parse_ident(input)?; + Ok(Some(Ident { + interface: Some(i), + item: ident, + })) + } else { + // We parsed the function ident as the interface ident + // Map the interface ident to the function ident + Ok(Some(Ident { + interface: None, + item: i.interface, + })) + } + } + Some(i) => { + // if we parse an interface id with a full package, we must + // be expecting a `#` next with the function ident + match input.pop_front() { + Some(t) if t.token() == TokenKind::Hash => { + let ident = Literal::parse_ident(input)?; + Ok(Some(Ident { + interface: Some(i), + item: ident, + })) + } + Some(t) => Err(ParserError::UnexpectedToken(t)), + None => Err(ParserError::UnexpectedEndOfInput), + } + } + + None => Ok(None), + } + } +} + +impl std::fmt::Display for Ident<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(interface) = self.interface { + write!(f, "{interface}#")? + } + write!(f, "{}", self.item) + } +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct InterfaceIdent<'a> { + package: Option<(&'a str, &'a str)>, + interface: &'a str, +} + +impl<'a> InterfaceIdent<'a> { + fn try_parse<'b>(input: &'b mut VecDeque>) -> Result, ParserError<'a>> { + #[derive(Debug)] + enum State<'a> { + ExpectFirst, + ExpectColon(&'a str), + ExpectSecond(&'a str), + ExpectSlash(&'a str, &'a str), + ExpectThird(&'a str, &'a str), + } + let mut state = State::ExpectFirst; + loop { + let token = input.front(); + match (token.map(|t| t.token()), state) { + (Some(TokenKind::Ident(i)), State::ExpectFirst) => { + input.pop_front(); + state = State::ExpectColon(i); + } + (Some(TokenKind::Colon), State::ExpectColon(first)) => { + input.pop_front(); + state = State::ExpectSecond(first); + } + (Some(TokenKind::Ident(second)), State::ExpectSecond(first)) => { + input.pop_front(); + state = State::ExpectSlash(first, second); + } + (Some(TokenKind::Slash), State::ExpectSlash(first, second)) => { + input.pop_front(); + state = State::ExpectThird(first, second); + } + (Some(TokenKind::Ident(third)), State::ExpectThird(first, second)) => { + input.pop_front(); + return Ok(Some(InterfaceIdent { + package: Some((first, second)), + interface: third, + })); + } + (_, State::ExpectColon(first)) => { + return Ok(Some(InterfaceIdent { + package: None, + interface: first, + })); + } + (_, State::ExpectFirst) => return Ok(None), + (Some(_), _) => return Err(ParserError::UnexpectedToken(*token.unwrap())), + _ => return Err(ParserError::UnexpectedEndOfInput), + } + } + } +} + +impl std::fmt::Display for InterfaceIdent<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some((namespace, package)) = self.package { + write!(f, "{namespace}:{package}/")?; + } + write!(f, "{}", self.interface) + } +} + +#[derive(Debug, PartialEq)] +pub enum Literal<'a> { + String(&'a str), + Number(usize), + List(List<'a>), + Record(Record<'a>), +} + +impl<'a> Literal<'a> { + fn parse_ident(input: &mut VecDeque>) -> Result<&'a str, ParserError<'a>> { + let Some(token) = input.front() else { + return Err(ParserError::UnexpectedEndOfInput); + }; + match token.token() { + TokenKind::Ident(i) => { + input.pop_front(); + Ok(i) + } + _ => Err(ParserError::UnexpectedToken(*token)), + } + } +} + +#[derive(Debug, PartialEq)] +pub struct List<'a> { + pub items: Vec>, +} + +impl<'a> From>> for List<'a> { + fn from(items: Vec>) -> Self { + Self { items } + } +} + +#[derive(Debug, PartialEq)] +pub struct Record<'a> { + pub fields: Vec<(&'a str, Expr<'a>)>, +} + +impl<'a> From)>> for Record<'a> { + fn from(fields: Vec<(&'a str, Expr<'a>)>) -> Self { + Self { fields } + } +} + +#[cfg(test)] +mod tests { + use std::vec; + + use crate::command::tokenizer::{SpannedStr, TokenKind}; + + use super::*; + + fn dummy_spanned_str() -> SpannedStr<'static> { + SpannedStr { str: "", offset: 0 } + } + + fn token(kind: TokenKind<'static>) -> Token<'static> { + Token { + input: dummy_spanned_str(), + token: kind, + } + } + + fn tokens(tokens: impl IntoIterator>) -> VecDeque> { + tokens.into_iter().map(token).collect() + } + + fn parse( + ts: impl IntoIterator>, + ) -> Result, ParserError<'static>> { + Line::parse(tokens(ts)) + } + + #[test] + fn parse_string_literals() { + let line = parse([TokenKind::String("hello-world")]).unwrap(); + assert_eq!( + line, + Line::Expr(Expr::Literal(Literal::String("hello-world"))) + ); + } + + #[test] + fn parse_list_literals() { + let list_of_string = Line::Expr(Expr::Literal(Literal::List( + vec![Expr::Literal(Literal::String("hello-world"))].into(), + ))); + let line = parse([ + TokenKind::OpenBracket, + TokenKind::String("hello-world"), + TokenKind::ClosedBracket, + ]) + .unwrap(); + assert_eq!(line, list_of_string); + + let line = parse([ + TokenKind::OpenBracket, + TokenKind::String("hello-world"), + TokenKind::Comma, + TokenKind::ClosedBracket, + ]) + .unwrap(); + assert_eq!(line, list_of_string); + + let err = parse([ + TokenKind::OpenBracket, + TokenKind::String("hello-world"), + TokenKind::Comma, + ]) + .unwrap_err(); + assert_eq!(err, ParserError::UnexpectedEndOfInput); + } + + #[test] + fn parse_record_literals() { + let record = Line::Expr(Expr::Literal(Literal::Record( + vec![("foo", Expr::Literal(Literal::String("bar")))].into(), + ))); + let line = parse([ + TokenKind::OpenBrace, + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::String("bar"), + TokenKind::ClosedBrace, + ]) + .unwrap(); + assert_eq!(line, record); + + let line = parse([ + TokenKind::OpenBrace, + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::String("bar"), + TokenKind::Comma, + TokenKind::ClosedBrace, + ]) + .unwrap(); + assert_eq!(line, record); + + let err = parse([ + TokenKind::OpenBrace, + TokenKind::Ident("foo"), + TokenKind::String("bar"), + TokenKind::ClosedBrace, + ]) + .unwrap_err(); + assert_eq!( + err, + ParserError::UnexpectedToken(token(TokenKind::String("bar"))) + ); + } + + #[test] + fn parse_function_calls() { + let function = Line::Expr(Expr::FunctionCall(FunctionCall { + ident: Ident { + interface: Some(InterfaceIdent { + package: Some(("foo", "bar")), + interface: "baz", + }), + item: "qux", + }, + args: vec![], + })); + let line = parse([ + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::Ident("bar"), + TokenKind::Slash, + TokenKind::Ident("baz"), + TokenKind::Hash, + TokenKind::Ident("qux"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + ]) + .unwrap(); + assert_eq!(line, function); + + let function = Line::Expr(Expr::FunctionCall(FunctionCall { + ident: Ident { + interface: None, + item: "qux", + }, + args: vec![], + })); + let line = parse([ + TokenKind::Ident("qux"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + ]) + .unwrap(); + assert_eq!(line, function); + + let function = Line::Expr(Expr::FunctionCall(FunctionCall { + ident: Ident { + interface: None, + item: "foo", + }, + args: vec![Expr::FunctionCall(FunctionCall { + ident: Ident { + interface: None, + item: "bar", + }, + args: vec![], + })], + })); + let line = parse([ + TokenKind::Ident("foo"), + TokenKind::OpenParen, + TokenKind::Ident("bar"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + TokenKind::ClosedParen, + ]) + .unwrap(); + assert_eq!(line, function); + + let err = parse([ + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::Ident("bar"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + ]) + .unwrap_err(); + assert_eq!( + err, + ParserError::UnexpectedToken(token(TokenKind::OpenParen)) + ); + + let err = parse([ + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::Ident("bar"), + TokenKind::Hash, + TokenKind::Ident("baz"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + ]) + .unwrap_err(); + assert_eq!(err, ParserError::UnexpectedToken(token(TokenKind::Hash))); + } + + #[test] + fn parse_ident_expr() { + let line = parse([TokenKind::Ident("foo")]).unwrap(); + assert_eq!(line, Line::Expr(Expr::Ident("foo"))); + } + + #[test] + fn parse_builtin() { + let line = parse([TokenKind::Builtin("foo"), TokenKind::Ident("foo")]).unwrap(); + assert_eq!( + line, + Line::BuiltIn(BuiltIn { + name: "foo", + rest: vec![token(TokenKind::Ident("foo"))] + }) + ); + } + + #[test] + fn parse_assignment() { + let line = parse([ + TokenKind::Ident("foo"), + TokenKind::Equal, + TokenKind::String("bar"), + ]) + .unwrap(); + assert_eq!( + line, + Line::Assignment("foo", Expr::Literal(Literal::String("bar"))) + ); + } +} diff --git a/src/command/tokenizer.rs b/src/command/tokenizer.rs new file mode 100644 index 0000000..f4f6e44 --- /dev/null +++ b/src/command/tokenizer.rs @@ -0,0 +1,254 @@ +use std::{collections::VecDeque, fmt::Write, ops::Deref}; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Token<'a> { + pub input: SpannedStr<'a>, + pub token: TokenKind<'a>, +} + +impl<'a> Token<'a> { + pub fn token(&self) -> TokenKind<'a> { + self.token + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum TokenKind<'a> { + String(&'a str), + Ident(&'a str), + Builtin(&'a str), + Flag(&'a str), + Number(usize), + Equal, + OpenParen, + ClosedParen, + Slash, + Hash, + Colon, + OpenBracket, + ClosedBracket, + OpenBrace, + ClosedBrace, + Comma, + Period, +} + +impl<'a> Token<'a> { + pub fn tokenize(input: &'a str) -> Result>, TokenizeError> { + let mut tokens = VecDeque::new(); + let mut rest = SpannedStr { + str: input, + offset: 0, + }; + while !rest.is_empty() { + let (new_rest, token) = Token::next(rest)?; + if let Some(token) = token { + tokens.push_back(token); + } + rest = new_rest; + } + Ok(tokens) + } + + fn next(rest: SpannedStr<'a>) -> Result<(SpannedStr<'a>, Option>), TokenizeError> { + let mut chars = rest.chars().peekable(); + let original_offset = rest.offset; + let Some(first) = chars.next() else { + panic!("TODO") + }; + let (offset, token_kind) = match first { + '"' => { + let len: usize = chars.take_while(|c| *c != '"').map(|c| c.len_utf8()).sum(); + let offset = 2 * '"'.len_utf8() + len; + let str = &rest.str[1..(offset - 1)]; + (offset, Some(TokenKind::String(str))) + } + c if c.is_ascii_alphabetic() => { + let len: usize = chars + .take_while(|c| c.is_ascii_alphabetic() || *c == '-') + .map(|c| c.len_utf8()) + .sum(); + let offset = c.len_utf8() + len; + let str = &rest.str[..offset]; + if str.ends_with('-') { + return Err(TokenizeError::UnexpectedChar( + '-', + original_offset + offset - 1, + )); + } + (offset, Some(TokenKind::Ident(str))) + } + c if c.is_ascii_digit() => { + let len: usize = chars + .take_while(|c| c.is_ascii_digit()) + .map(|c| c.len_utf8()) + .sum(); + let offset = c.len_utf8() + len; + let num = rest.str[..offset].parse().expect("TODO"); + (offset, Some(TokenKind::Number(num))) + } + c if c.is_whitespace() => (c.len_utf8(), None), + '=' => ('='.len_utf8(), Some(TokenKind::Equal)), + '(' => ('('.len_utf8(), Some(TokenKind::OpenParen)), + ')' => (')'.len_utf8(), Some(TokenKind::ClosedParen)), + '/' => ('/'.len_utf8(), Some(TokenKind::Slash)), + '#' => ('/'.len_utf8(), Some(TokenKind::Hash)), + ':' => ('/'.len_utf8(), Some(TokenKind::Colon)), + '[' => ('['.len_utf8(), Some(TokenKind::OpenBracket)), + ']' => (']'.len_utf8(), Some(TokenKind::ClosedBracket)), + ',' => (','.len_utf8(), Some(TokenKind::Comma)), + '.' => { + if matches!(chars.peek(), Some(c) if c.is_alphabetic()) { + let len: usize = chars + .take_while(|c| c.is_ascii_alphabetic() || *c == '_') + .map(|c| c.len_utf8()) + .sum(); + let offset = '.'.len_utf8() + len; + let ident = &rest.str[1..offset]; + (offset, Some(TokenKind::Builtin(ident))) + } else { + ('.'.len_utf8(), Some(TokenKind::Period)) + } + } + '{' => ('.'.len_utf8(), Some(TokenKind::OpenBrace)), + '}' => ('.'.len_utf8(), Some(TokenKind::ClosedBrace)), + '-' if chars.peek() == Some(&'-') => { + let len: usize = chars + .skip(1) + .take_while(|c| c.is_ascii_alphabetic() || *c == '_' || *c == '-') + .map(|c| c.len_utf8()) + .sum(); + let offset = '-'.len_utf8() * 2 + len; + let ident = &rest.str[2..offset]; + (offset, Some(TokenKind::Flag(ident))) + } + _ => return Err(TokenizeError::UnexpectedChar(first, original_offset)), + }; + Ok(( + rest.offset(offset), + token_kind.map(|token| Token { + input: SpannedStr { + str: &rest.str[..offset], + offset: original_offset, + }, + token, + }), + )) + } +} + +#[derive(Debug, PartialEq)] +pub enum TokenizeError { + UnexpectedChar(char, usize), +} + +impl std::error::Error for TokenizeError {} + +impl std::fmt::Display for TokenizeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TokenizeError::UnexpectedChar(char, _) => { + f.write_str("unexpected character: ")?; + f.write_char(*char) + } + } + } +} + +/// A view into the input str with span information +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct SpannedStr<'a> { + pub str: &'a str, + pub offset: usize, +} + +impl<'a> SpannedStr<'a> { + fn offset(self, offset: usize) -> SpannedStr<'a> { + Self { + str: &self.str[offset..], + offset: self.offset + offset, + } + } +} + +impl Deref for SpannedStr<'_> { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.str + } +} + +#[cfg(test)] +mod tests { + pub use super::*; + #[test] + fn tokenize_literals() { + let input = r#" "hello-world" "#; + let tokens = Token::tokenize(input).unwrap(); + assert_eq!(tokens.len(), 1); + assert_eq!( + tokens[0], + Token { + input: SpannedStr { + str: r#""hello-world""#, + offset: 2 + }, + token: TokenKind::String("hello-world") + } + ); + + let input = " hello "; + let tokens = Token::tokenize(input).unwrap(); + assert_eq!(tokens.len(), 1); + assert_eq!( + tokens[0], + Token { + input: SpannedStr { + str: "hello", + offset: 2 + }, + token: TokenKind::Ident("hello") + } + ); + } + + #[test] + fn tokenize_ident() { + let input = " hello- "; + let err = Token::tokenize(input).unwrap_err(); + assert_eq!(err, TokenizeError::UnexpectedChar('-', 7)) + } + + #[test] + fn tokenize_assignment() { + let input = r#" hello = "world" "#; + let tokens = Token::tokenize(input) + .unwrap() + .into_iter() + .map(|t| t.token) + .collect::>(); + assert_eq!( + tokens, + vec![ + TokenKind::Ident("hello"), + TokenKind::Equal, + TokenKind::String("world"), + ] + ) + } + + #[test] + fn tokenize_builtin() { + let input = ".foo hello"; + let tokens = Token::tokenize(input) + .unwrap() + .into_iter() + .map(|t| t.token) + .collect::>(); + assert_eq!( + tokens, + vec![TokenKind::Builtin("foo"), TokenKind::Ident("hello"),] + ) + } +} diff --git a/src/evaluator.rs b/src/evaluator.rs index d581b3b..58eab1a 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -3,11 +3,7 @@ use std::collections::HashMap; use anyhow::{bail, Context}; use wasmtime::component::{self, List, Record, Val}; -use crate::{ - command::parser::{self, FunctionIdent}, - runtime::Runtime, - wit::WorldResolver, -}; +use crate::{command::alt_parser, runtime::Runtime, wit::WorldResolver}; pub struct Evaluator<'a> { runtime: &'a mut Runtime, @@ -32,13 +28,15 @@ impl<'a> Evaluator<'a> { /// Evaluate the expression with the provided type hint pub fn eval( &mut self, - expr: parser::Expr<'_>, + expr: alt_parser::Expr<'_>, type_hint: Option<&component::Type>, ) -> anyhow::Result { match expr { - parser::Expr::Literal(l) => self.eval_literal(l, type_hint), - parser::Expr::Ident(ident) => self.resolve_ident(&*ident, type_hint), - parser::Expr::FunctionCall(ident, mut args) => { + alt_parser::Expr::Literal(l) => self.eval_literal(l, type_hint), + alt_parser::Expr::Ident(ident) => self.resolve_ident(&*ident, type_hint), + alt_parser::Expr::FunctionCall(func) => { + let ident = func.ident; + let mut args = func.args; log::debug!( "Checking for type constructor for {ident} #args={} type_hint={type_hint:?}", args.len() @@ -46,22 +44,20 @@ impl<'a> Evaluator<'a> { // If the preferred type has some sort of type constructor, try that first match type_hint { Some(component::Type::Option(o)) - if ident.interface.is_none() - && ident.function == "some" - && args.len() == 1 => + if ident.interface.is_none() && ident.item == "some" && args.len() == 1 => { let val = self.eval(args.remove(0), Some(&o.ty()))?; return o.new_val(Some(val)); } Some(component::Type::Result(r)) if args.len() == 1 => { if let Some(ok) = r.ok() { - if ident.interface.is_none() && ident.function == "ok" { + if ident.interface.is_none() && ident.item == "ok" { let val = self.eval(args.remove(0), Some(&ok))?; return r.new_val(Ok(Some(val))); } } if let Some(err) = r.err() { - if ident.interface.is_none() && ident.function == "err" { + if ident.interface.is_none() && ident.item == "err" { let val = self.eval(args.remove(0), Some(&err))?; return r.new_val(Err(Some(val))); } @@ -85,8 +81,8 @@ impl<'a> Evaluator<'a> { /// Call the function pub fn call_func( &mut self, - ident: FunctionIdent, - args: Vec>, + ident: alt_parser::Ident, + args: Vec>, ) -> anyhow::Result> { log::debug!("Calling function: {ident} with args: {args:?}"); let func_def = self @@ -119,11 +115,11 @@ impl<'a> Evaluator<'a> { /// Evaluate a literal using the provided type hint pub fn eval_literal( &mut self, - literal: parser::Literal<'_>, + literal: alt_parser::Literal<'_>, type_hint: Option<&component::Type>, ) -> anyhow::Result { match literal { - parser::Literal::List(list) => { + alt_parser::Literal::List(list) => { match type_hint { Some(component::Type::List(l)) => { let mut values = Vec::new(); @@ -158,7 +154,7 @@ impl<'a> Evaluator<'a> { } } } - parser::Literal::Record(mut r) => { + alt_parser::Literal::Record(mut r) => { let ty = match type_hint { Some(component::Type::Record(r)) => r, Some(t) => bail!( @@ -177,20 +173,16 @@ impl<'a> Evaluator<'a> { .map(|(index, field)| (field.name, index)) .collect::>(); // Sort the fields since wasmtime expects the fields to be in the defined order - r.fields.sort_by(|(f1, _), (f2, _)| { - types - .get(f1.as_str()) - .unwrap() - .cmp(types.get(f2.as_str()).unwrap()) - }); + r.fields + .sort_by(|(f1, _), (f2, _)| types.get(f1).unwrap().cmp(types.get(f2).unwrap())); for ((name, field_expr), field_type) in r.fields.into_iter().zip(ty.fields()) { - values.push((name.as_str(), self.eval(field_expr, Some(&field_type.ty))?)); + values.push((name, self.eval(field_expr, Some(&field_type.ty))?)); } Ok(Val::Record(Record::new(ty, values)?)) } - parser::Literal::String(s) => { - let val = Val::String(s.as_str().to_owned().into()); + alt_parser::Literal::String(s) => { + let val = Val::String(s.to_owned().into()); match type_hint { Some(component::Type::Result(r)) => r.new_val(match (r.ok(), r.err()) { (Some(_), _) => Ok(Some(val)), @@ -200,7 +192,7 @@ impl<'a> Evaluator<'a> { _ => Ok(val), } } - parser::Literal::Num(n) => match type_hint { + alt_parser::Literal::Number(n) => match type_hint { Some(component::Type::U8) => Ok(Val::U8(n.try_into()?)), _ => Ok(Val::S32(n.try_into()?)), }, diff --git a/src/runtime.rs b/src/runtime.rs index 43a9412..3ee552f 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -14,9 +14,10 @@ use wasmtime_wasi::preview2::{ WasiView, }; -use crate::command::parser::{FunctionIdent, InterfaceIdent, ItemIdent}; - -use super::wit::WorldResolver; +use crate::{ + command::alt_parser::{self, Ident}, + wit::WorldResolver, +}; pub struct Runtime { engine: Engine, @@ -97,7 +98,7 @@ impl Runtime { }) } - pub fn get_func(&mut self, ident: FunctionIdent) -> anyhow::Result { + pub fn get_func(&mut self, ident: Ident) -> anyhow::Result { let func = match ident.interface { Some(i) => { let mut exports = self.instance.exports(&mut self.store); @@ -107,13 +108,13 @@ impl Runtime { .with_context(|| { format!("could not find exported instance with name '{instance_name}'") })? - .func(&ident.function) + .func(&ident.item) } None => self .instance .exports(&mut self.store) .root() - .func(&ident.function), + .func(&ident.item), }; func.with_context(|| format!("could not find function '{ident}' in instance")) } @@ -137,21 +138,23 @@ impl Runtime { pub fn stub( &mut self, querier: &WorldResolver, - import_ident: ItemIdent<'_>, - export_ident: ItemIdent<'_>, + import_ident: alt_parser::ItemIdent<'_>, + export_ident: alt_parser::ItemIdent<'_>, component_bytes: &[u8], ) -> anyhow::Result<()> { match (import_ident, export_ident) { - (ItemIdent::Function(import_ident), ItemIdent::Function(export_ident)) => { - self.stub_function(querier, import_ident, export_ident, component_bytes) - } - (ItemIdent::Interface(import_ident), ItemIdent::Interface(export_ident)) => { - self.stub_interface(querier, import_ident, export_ident, component_bytes) - } - (ItemIdent::Interface(_), ItemIdent::Function(_)) => { + ( + alt_parser::ItemIdent::Item(import_ident), + alt_parser::ItemIdent::Item(export_ident), + ) => self.stub_function(querier, import_ident, export_ident, component_bytes), + ( + alt_parser::ItemIdent::Interface(import_ident), + alt_parser::ItemIdent::Interface(export_ident), + ) => self.stub_interface(querier, import_ident, export_ident, component_bytes), + (alt_parser::ItemIdent::Interface(_), alt_parser::ItemIdent::Item(_)) => { anyhow::bail!("cannot satisfy interface import with a function") } - (ItemIdent::Function(_), ItemIdent::Interface(_)) => { + (alt_parser::ItemIdent::Item(_), alt_parser::ItemIdent::Interface(_)) => { anyhow::bail!("cannot satisfy function import with an interface") } } @@ -160,8 +163,8 @@ impl Runtime { pub fn stub_interface( &mut self, querier: &WorldResolver, - import_ident: InterfaceIdent<'_>, - export_ident: InterfaceIdent<'_>, + import_ident: alt_parser::InterfaceIdent<'_>, + export_ident: alt_parser::InterfaceIdent<'_>, component_bytes: &[u8], ) -> anyhow::Result<()> { let component = load_component(&self.engine, component_bytes)?; @@ -255,8 +258,8 @@ impl Runtime { pub fn stub_function( &mut self, querier: &WorldResolver, - import_ident: FunctionIdent<'_>, - export_ident: FunctionIdent<'_>, + import_ident: alt_parser::Ident<'_>, + export_ident: alt_parser::Ident<'_>, component_bytes: &[u8], ) -> anyhow::Result<()> { // type checking @@ -286,15 +289,15 @@ impl Runtime { let mut instance = export .instance(&interface.to_string()) .with_context(|| format!("no export named '{interface} found'"))?; - instance.func(&export_ident.function) + instance.func(&export_ident.item) } - None => export_instance.get_func(&mut *store_lock, &export_ident.function), + None => export_instance.get_func(&mut *store_lock, &export_ident.item), } } .with_context(|| format!("no function found named '{export_ident}'"))?; let store = self.import_impls.store.clone(); - let name = import_ident.function.as_str().to_owned(); + let name = import_ident.item.to_owned(); match import_ident.interface { Some(interface) => { let mut instance = self diff --git a/src/wit.rs b/src/wit.rs index b0bf802..b79b511 100644 --- a/src/wit.rs +++ b/src/wit.rs @@ -7,7 +7,7 @@ use wit_parser::{ WorldKey, }; -use crate::command::parser::{FunctionIdent, InterfaceIdent}; +use crate::command::alt_parser; /// A resolver for a wit world. pub struct WorldResolver { @@ -38,14 +38,14 @@ impl WorldResolver { } /// Get the exported function by the given `FunctionIdent`. - pub fn exported_function(&self, ident: FunctionIdent) -> Option<&Function> { + pub fn exported_function(&self, ident: alt_parser::Ident) -> Option<&Function> { match ident.interface { Some(i) => { let interface = self.exported_interface(i)?; - interface.functions.get(ident.function.as_str()) + interface.functions.get(ident.item) } None => { - if let WorldItem::Function(f) = &self.export(ident.function.as_str())? { + if let WorldItem::Function(f) = &self.export(ident.item)? { Some(f) } else { None @@ -55,14 +55,14 @@ impl WorldResolver { } /// Get the imported function by the given `FunctionIdent`. - pub fn imported_function(&self, ident: FunctionIdent) -> Option<&Function> { + pub fn imported_function(&self, ident: alt_parser::Ident) -> Option<&Function> { match ident.interface { Some(i) => { let interface = self.imported_interface(i)?; - interface.functions.get(ident.function.as_str()) + interface.functions.get(ident.item) } None => { - if let WorldItem::Function(f) = &self.import(ident.function.as_str())? { + if let WorldItem::Function(f) = &self.import(ident.item)? { Some(f) } else { None @@ -72,12 +72,12 @@ impl WorldResolver { } /// Get the exported interface by the given `InterfaceIdent`. - pub fn exported_interface(&self, ident: InterfaceIdent) -> Option<&Interface> { + pub fn exported_interface(&self, ident: alt_parser::InterfaceIdent) -> Option<&Interface> { self.interface_in_items(ident, self.world().exports.iter()) } /// Get the imported interface by the given `InterfaceIdent`. - pub fn imported_interface(&self, ident: InterfaceIdent) -> Option<&Interface> { + pub fn imported_interface(&self, ident: alt_parser::InterfaceIdent) -> Option<&Interface> { self.interface_in_items(ident, self.world().imports.iter()) } @@ -342,7 +342,7 @@ impl WorldResolver { /// Get an interface by its ident from a list of world items. fn interface_in_items<'a>( &self, - ident: InterfaceIdent, + ident: alt_parser::InterfaceIdent, items: impl Iterator, ) -> Option<&Interface> { let item = self.get_world_item_by_name(items, &ident.to_string())?; From 032b5b0e9bd91cd71a5b4514fd05d7aac30ee9f2 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Tue, 12 Mar 2024 22:51:50 +0100 Subject: [PATCH 2/2] Move to new module Signed-off-by: Ryan Levick --- src/command.rs | 155 ++++--- src/command/alt_parser.rs | 674 ---------------------------- src/command/parser.rs | 905 +++++++++++++++++++++++--------------- src/command/tokenizer.rs | 6 +- src/evaluator.rs | 32 +- src/main.rs | 8 +- src/runtime.rs | 92 ++-- src/wit.rs | 12 +- 8 files changed, 699 insertions(+), 1185 deletions(-) delete mode 100644 src/command/alt_parser.rs diff --git a/src/command.rs b/src/command.rs index 067722d..9439393 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,4 +1,3 @@ -pub mod alt_parser; pub mod parser; mod tokenizer; use std::collections::HashMap; @@ -7,7 +6,7 @@ use anyhow::{bail, Context as _}; use colored::Colorize; use wasmtime::component::Val; -use self::alt_parser::ItemIdent; +use self::parser::Ident; use self::tokenizer::TokenKind; use super::runtime::Runtime; @@ -20,23 +19,22 @@ pub enum Cmd<'a> { name: &'a str, args: Vec>, }, - Eval(alt_parser::Expr<'a>), + Eval(parser::Expr<'a>), Assign { ident: &'a str, - value: alt_parser::Expr<'a>, + value: parser::Expr<'a>, }, } impl<'a> Cmd<'a> { pub fn parse(input: &'a str) -> anyhow::Result>> { let tokens = tokenizer::Token::tokenize(input)?; - let line = alt_parser::Line::parse(tokens) - .expect("TODO: using ? here leads to borrow checker errors"); + let line = parser::Line::parse(tokens).map_err(|e| anyhow::anyhow!("{e}"))?; log::debug!("Parsed line: {line:?}"); match line { - alt_parser::Line::Expr(expr) => Ok(Some(Cmd::Eval(expr))), - alt_parser::Line::Assignment(ident, value) => Ok(Some(Cmd::Assign { ident, value })), - alt_parser::Line::BuiltIn(builtin) => Ok(Some(Cmd::BuiltIn { + parser::Line::Expr(expr) => Ok(Some(Cmd::Eval(expr))), + parser::Line::Assignment(ident, value) => Ok(Some(Cmd::Assign { ident, value })), + parser::Line::BuiltIn(builtin) => Ok(Some(Cmd::BuiltIn { name: builtin.name, args: builtin.rest, })), @@ -49,17 +47,17 @@ impl<'a> Cmd<'a> { pub fn run( self, runtime: &mut Runtime, - querier: &mut WorldResolver, + resolver: &mut WorldResolver, scope: &mut HashMap, ) -> anyhow::Result { - let mut eval = Evaluator::new(runtime, querier, scope); + let mut eval = Evaluator::new(runtime, resolver, scope); match self { Cmd::Eval(expr) => match expr { - alt_parser::Expr::Literal(l) => { + parser::Expr::Literal(l) => { let val = eval.eval_literal(l, None)?; println!("{}: {}", format_val(&val), val_as_type(&val)); } - alt_parser::Expr::Ident(ident) => match scope.get(ident) { + parser::Expr::Ident(ident) => match scope.get(ident) { Some(val) => { println!("{}: {}", format_val(val), val_as_type(val)) } @@ -67,7 +65,7 @@ impl<'a> Cmd<'a> { anyhow::bail!("no identifier '{ident}' in scope") } }, - alt_parser::Expr::FunctionCall(func) => { + parser::Expr::FunctionCall(func) => { let results = eval.call_func(func.ident, func.args)?; println!( "{}", @@ -84,16 +82,19 @@ impl<'a> Cmd<'a> { println!("{}: {}", ident, val_as_type(&val)); scope.insert(ident.into(), val); } - Cmd::BuiltIn { name, args } if name == "exports" => { + Cmd::BuiltIn { + name: "exports", + args, + } => { let &[] = args.as_slice() else { bail!( "wrong number of arguments to exports function. Expected 0 got {}", args.len() ) }; - for (export_name, export) in querier.world().exports.iter() { - let export_name = querier.world_item_name(export_name); - if let Some(ty) = format_world_item(export, querier) { + for (export_name, export) in resolver.world().exports.iter() { + let export_name = resolver.world_item_name(export_name); + if let Some(ty) = format_world_item(export, resolver) { println!("{}: {ty}", export_name.bold()); } } @@ -118,36 +119,42 @@ impl<'a> Cmd<'a> { ) } }; - for (import_name, import) in querier.imports(include_wasi) { - let import_name = querier.world_item_name(import_name); - if let Some(ty) = format_world_item(import, querier) { + for (import_name, import) in resolver.imports(include_wasi) { + let import_name = resolver.world_item_name(import_name); + if let Some(ty) = format_world_item(import, resolver) { println!("{}: {ty}", import_name.bold()); } } } - // Cmd::BuiltIn { name: "type", args } => { - // match args.as_slice() { - // &[name] => { - // let types = querier.types_by_name(&*name); - // for (interface, ty) in &types { - // let typ = querier.display_wit_type_def(ty, Expansion::Expanded(1)); - // let name = &ty.name; - // let interface = interface.and_then(|i| querier.interface_name(i)); - // let ident = match (interface, name) { - // (Some(i), Some(n)) => format!("{i}#{n}: "), - // (None, Some(n)) => format!("{n}: "), - // _ => todo!(), - // }; - // println!("{ident}{typ}"); - // } - // } - // _ => bail!( - // "wrong number of arguments to inspect function. Expected 1 got {}", - // args.len() - // ), - // }; - // } - Cmd::BuiltIn { name, args } if name == "compose" => { + Cmd::BuiltIn { name: "type", args } => { + match args.as_slice() { + &[token] => { + let TokenKind::Ident(name) = token.token() else { + bail!("unrecognized token") + }; + let types = resolver.types_by_name(name); + for (interface, ty) in &types { + let typ = resolver.display_wit_type_def(ty, Expansion::Expanded(1)); + let name = &ty.name; + let interface = interface.and_then(|i| resolver.interface_name(i)); + let ident = match (interface, name) { + (Some(i), Some(n)) => format!("{i}#{n}: "), + (None, Some(n)) => format!("{n}: "), + _ => todo!(), + }; + println!("{ident}{typ}"); + } + } + _ => bail!( + "wrong number of arguments to inspect function. Expected 1 got {}", + args.len() + ), + }; + } + Cmd::BuiltIn { + name: "compose", + args, + } => { let &[token] = args.as_slice() else { bail!( "wrong number of arguments to compose function. Expected 1 got {}", @@ -160,48 +167,48 @@ impl<'a> Cmd<'a> { let adapter = std::fs::read(&*path).context("could not read path to adapter module")?; runtime.compose(&adapter)?; - *querier = WorldResolver::from_bytes(runtime.component_bytes())?; + *resolver = WorldResolver::from_bytes(runtime.component_bytes())?; } - Cmd::BuiltIn { name, args } if name == "link" => { + Cmd::BuiltIn { name: "link", args } => { let mut args = args.into_iter().collect(); - let Ok(Some(import_ident)) = ItemIdent::try_parse(&mut args) else { + let Ok(Some(import_ident)) = Ident::try_parse(&mut args) else { bail!("import_ident is not a proper item identifier"); }; - let Ok(Some(export_ident)) = ItemIdent::try_parse(&mut args) else { + let Ok(Some(export_ident)) = Ident::try_parse(&mut args) else { bail!("export_ident is not a proper item identifier"); }; let Some(TokenKind::String(component)) = args.pop_front().map(|t| t.token()) else { - bail!("TODO"); + bail!("component path is not a string"); }; let component_bytes = std::fs::read(component) .with_context(|| format!("could not read component '{component}'"))?; - runtime.stub(&querier, import_ident, export_ident, &component_bytes)?; + runtime.stub(&resolver, import_ident, export_ident, &component_bytes)?; } Cmd::BuiltIn { name: "inspect", args, } => { let mut args = args.into_iter().collect(); - let Ok(Some(ident)) = ItemIdent::try_parse(&mut args) else { + let Ok(Some(ident)) = Ident::try_parse(&mut args) else { bail!("ident is not a proper item identifier"); }; match ident { - ItemIdent::Item(ident) => { - let f = querier + Ident::Item(ident) => { + let f = resolver .exported_function(ident) - .or_else(|| querier.imported_function(ident)); + .or_else(|| resolver.imported_function(ident)); match f { - Some(f) => println!("{}", format_function(f, querier)), + Some(f) => println!("{}", format_function(f, resolver)), None => bail!("Could not find imported or exported function '{ident}'"), } } - ItemIdent::Interface(ident) => { - let i = querier + Ident::Interface(ident) => { + let i = resolver .exported_interface(ident) - .or_else(|| querier.imported_interface(ident)); + .or_else(|| resolver.imported_interface(ident)); match i { - Some(f) => println!("{}", format_interface(f, querier)), + Some(f) => println!("{}", format_interface(f, resolver)), None => { bail!("Could not find imported or exported interface '{ident}'") } @@ -209,8 +216,14 @@ impl<'a> Cmd<'a> { } } } - Cmd::BuiltIn { name, args: _ } if name == "help" => print_help(), - Cmd::BuiltIn { name, args: _ } if name == "clear" => return Ok(true), + Cmd::BuiltIn { + name: "help", + args: _, + } => print_help(), + Cmd::BuiltIn { + name: "clear", + args: _, + } => return Ok(true), Cmd::BuiltIn { name, args: _ } => { bail!("Unrecognized built-in function '{name}'") } @@ -236,22 +249,22 @@ There are also builtin functions that can be called with a preceding '.'. Suppor .inspect $item inspect an item `$item` in scope (`?` is alias for this built-in)") } -fn format_world_item(item: &wit_parser::WorldItem, querier: &WorldResolver) -> Option { +fn format_world_item(item: &wit_parser::WorldItem, resolver: &WorldResolver) -> Option { match item { - wit_parser::WorldItem::Function(f) => Some(format_function(f, querier)), + wit_parser::WorldItem::Function(f) => Some(format_function(f, resolver)), wit_parser::WorldItem::Interface(id) => { - let interface = querier.interface_by_id(*id).unwrap(); + let interface = resolver.interface_by_id(*id).unwrap(); if interface.functions.is_empty() { return None; } - let output = format_interface(interface, querier); + let output = format_interface(interface, resolver); Some(output) } wit_parser::WorldItem::Type(_) => None, } } -fn format_interface(interface: &wit_parser::Interface, querier: &WorldResolver) -> String { +fn format_interface(interface: &wit_parser::Interface, resolver: &WorldResolver) -> String { use std::fmt::Write; let mut output = String::from("{\n"); for (_, fun) in &interface.functions { @@ -259,7 +272,7 @@ fn format_interface(interface: &wit_parser::Interface, querier: &WorldResolver) &mut output, " {}: {}", fun.name.bold(), - format_function(fun, querier) + format_function(fun, resolver) ) .unwrap(); } @@ -267,16 +280,16 @@ fn format_interface(interface: &wit_parser::Interface, querier: &WorldResolver) output } -fn format_function(f: &wit_parser::Function, querier: &WorldResolver) -> String { +fn format_function(f: &wit_parser::Function, resolver: &WorldResolver) -> String { let mut params = Vec::new(); for (param_name, param_type) in &f.params { - let ty = querier.display_wit_type(param_type, Expansion::Collapsed); + let ty = resolver.display_wit_type(param_type, Expansion::Collapsed); params.push(format!("{param_name}: {}", ty.italic())); } let params = params.join(", "); let rets = match &f.results { wit_parser::Results::Anon(t) => { - let t = querier.display_wit_type(t, Expansion::Collapsed); + let t = resolver.display_wit_type(t, Expansion::Collapsed); format!(" -> {}", t.italic()) } wit_parser::Results::Named(n) if n.is_empty() => String::new(), @@ -284,7 +297,7 @@ fn format_function(f: &wit_parser::Function, querier: &WorldResolver) -> String let params = params .iter() .map(|(name, t)| { - let t = querier.display_wit_type(t, Expansion::Collapsed); + let t = resolver.display_wit_type(t, Expansion::Collapsed); format!("{name}: {t}") }) .collect::>() diff --git a/src/command/alt_parser.rs b/src/command/alt_parser.rs deleted file mode 100644 index 25a40b0..0000000 --- a/src/command/alt_parser.rs +++ /dev/null @@ -1,674 +0,0 @@ -use std::collections::VecDeque; - -use crate::command::tokenizer::TokenKind; - -use super::tokenizer::Token; - -#[derive(Debug, PartialEq)] -pub enum Line<'a> { - Expr(Expr<'a>), - BuiltIn(BuiltIn<'a>), - Assignment(&'a str, Expr<'a>), -} - -impl<'a> Line<'a> { - pub fn parse(mut tokens: VecDeque>) -> Result, ParserError<'a>> { - let result = match BuiltIn::try_parse(&mut tokens)? { - Some(builtin) => Ok(Self::BuiltIn(builtin)), - None => match Self::try_parse_assignment(&mut tokens)? { - Some((ident, expr)) => Ok(Self::Assignment(ident, expr)), - None => match Expr::try_parse(&mut tokens)? { - Some(e) => Ok(Self::Expr(e)), - None => todo!(), - }, - }, - }; - if !tokens.is_empty() { - return Err(ParserError::RemainingInput); - } - result - } - - fn try_parse_assignment( - tokens: &mut VecDeque>, - ) -> Result)>, ParserError<'a>> { - let Some(token) = tokens.front() else { - return Ok(None); - }; - println!("0 {tokens:?}"); - let TokenKind::Ident(ident) = token.token() else { - return Ok(None); - }; - println!("1 {tokens:?}"); - let token = *token; - let _ = tokens.pop_front(); - if matches!(tokens.front().map(|t| t.token()), Some(TokenKind::Equal)) { - let _ = tokens.pop_front(); - println!("{tokens:?}"); - match Expr::try_parse(tokens)? { - Some(e) => Ok(Some((ident, e))), - None => Err(ParserError::ExpectedExpr), - } - } else { - tokens.push_front(token); - Ok(None) - } - } -} - -#[derive(Debug, PartialEq)] -pub struct BuiltIn<'a> { - pub name: &'a str, - pub rest: Vec>, -} - -impl<'a> BuiltIn<'a> { - fn try_parse(tokens: &mut VecDeque>) -> Result>, ParserError<'a>> { - let Some(TokenKind::Builtin(ident)) = tokens.front().map(|t| t.token()) else { - return Ok(None); - }; - tokens.pop_front(); - Ok(Some(BuiltIn { - name: ident, - rest: tokens.drain(..).collect(), - })) - } -} - -#[derive(Debug, PartialEq)] -pub enum ParserError<'a> { - UnexpectedToken(Token<'a>), - UnexpectedEndOfInput, - RemainingInput, - ExpectedExpr, -} - -impl std::error::Error for ParserError<'_> {} - -impl std::fmt::Display for ParserError<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ParserError::UnexpectedToken(_) => f.write_str("unexpected token"), - ParserError::UnexpectedEndOfInput => f.write_str("unexpected end of input"), - ParserError::RemainingInput => f.write_str("remaining input"), - ParserError::ExpectedExpr => f.write_str("unexpected expression"), - } - } -} - -#[derive(Debug, PartialEq)] -pub enum Expr<'a> { - FunctionCall(FunctionCall<'a>), - Ident(&'a str), - Literal(Literal<'a>), -} - -impl<'a> Expr<'a> { - fn try_parse(input: &mut VecDeque>) -> Result>, ParserError<'a>> { - let Some(first) = input.front() else { - return Ok(None); - }; - match first.token() { - TokenKind::String(s) => { - input.pop_front(); - Ok(Some(Expr::Literal(Literal::String(s)))) - } - TokenKind::Number(n) => { - input.pop_front(); - Ok(Some(Expr::Literal(Literal::Number(n)))) - } - TokenKind::OpenBracket => { - input.pop_front(); - enum State { - ExpectExpr, - ExpectComma, - } - let mut state = State::ExpectExpr; - let mut items = vec![]; - while let Some(token) = input.front() { - match token.token() { - TokenKind::ClosedBracket => { - input.pop_front(); - return Ok(Some(Expr::Literal(Literal::List(List { items })))); - } - TokenKind::Comma if matches!(state, State::ExpectComma) => { - input.pop_front(); - state = State::ExpectExpr; - } - _ => { - let expr = Expr::try_parse(input)?; - if let Some(expr) = expr { - items.push(expr); - state = State::ExpectComma; - } else { - return Err(ParserError::UnexpectedEndOfInput); - } - } - } - } - return Err(ParserError::UnexpectedEndOfInput); - } - TokenKind::OpenBrace => { - input.pop_front(); - enum State<'a> { - ExpectIdent, - ExpectColon(&'a str), - ExpectExpr(&'a str), - ExpectComma, - } - let mut state = State::ExpectIdent; - let mut fields = vec![]; - while let Some(token) = input.front() { - match (token.token(), state) { - (TokenKind::ClosedBrace, State::ExpectComma | State::ExpectIdent) => { - input.pop_front(); - return Ok(Some(Expr::Literal(Literal::Record(Record { fields })))); - } - (TokenKind::Comma, State::ExpectComma) => { - input.pop_front(); - state = State::ExpectIdent; - } - (TokenKind::Colon, State::ExpectColon(ident)) => { - input.pop_front(); - state = State::ExpectExpr(ident); - } - (_, State::ExpectIdent) => { - let ident = Literal::parse_ident(input)?; - state = State::ExpectColon(ident); - } - (_, State::ExpectExpr(ident)) => { - let expr = Expr::try_parse(input)?; - if let Some(expr) = expr { - fields.push((ident, expr)); - state = State::ExpectComma; - } else { - return Err(ParserError::UnexpectedEndOfInput); - } - } - _ => return Err(ParserError::UnexpectedToken(*token)), - } - } - return Err(ParserError::UnexpectedEndOfInput); - } - TokenKind::Ident(_) => { - let func = FunctionCall::try_parse(input)?; - match func { - Some(f) => Ok(Some(Expr::FunctionCall(f))), - None => Ok(Some(Expr::Ident(Literal::parse_ident(input)?))), - } - } - - _ => return Ok(None), - } - } -} - -#[derive(Debug, PartialEq)] -pub struct FunctionCall<'a> { - pub ident: Ident<'a>, - pub args: Vec>, -} - -impl<'a> FunctionCall<'a> { - fn try_parse(input: &mut VecDeque>) -> Result, ParserError<'a>> { - let original = input.clone(); - let Some(function_ident) = Ident::try_parse(input)? else { - return Ok(None); - }; - let next = input.front(); - if next.map(|t| t.token()) != Some(TokenKind::OpenParen) { - // If we failed to find an open paren then we need to completely bail - // on function parsing which means restoring the input state back to - // its original form. - *input = original; - return Ok(None); - } - expect_token(input, |t| t == TokenKind::OpenParen)?; - let mut args = Vec::new(); - loop { - let Some(expr) = Expr::try_parse(input)? else { - break; - }; - args.push(expr); - if input.front().map(|t| t.token()) != Some(TokenKind::Comma) { - break; - } - } - expect_token(input, |t| t == TokenKind::ClosedParen)?; - Ok(Some(FunctionCall { - ident: function_ident, - args, - })) - } -} - -fn expect_token<'a>( - input: &mut VecDeque>, - pred: impl FnOnce(TokenKind<'a>) -> bool, -) -> Result<(), ParserError<'a>> { - let Some(token) = input.pop_front() else { - return Err(ParserError::UnexpectedEndOfInput); - }; - if !pred(token.token()) { - return Err(ParserError::UnexpectedToken(token)); - } - Ok(()) -} - -// TODO: Rename this to Ident and Ident to ItemIdent -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum ItemIdent<'a> { - Item(Ident<'a>), - Interface(InterfaceIdent<'a>), -} - -impl<'a> ItemIdent<'a> { - pub(crate) fn try_parse( - input: &mut VecDeque>, - ) -> Result>, ParserError<'a>> { - match Ident::try_parse(input)? { - Some(i) => Ok(Some(Self::Item(i))), - None => Ok(InterfaceIdent::try_parse(input)?.map(Self::Interface)), - } - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -pub struct Ident<'a> { - pub interface: Option>, - pub item: &'a str, -} - -impl<'a> Ident<'a> { - fn try_parse(input: &mut VecDeque>) -> Result, ParserError<'a>> { - let interface = InterfaceIdent::try_parse(input)?; - match interface { - Some(i) if i.package.is_none() => { - if input.front().map(|t| t.token()) == Some(TokenKind::Hash) { - input.pop_front(); - let ident = Literal::parse_ident(input)?; - Ok(Some(Ident { - interface: Some(i), - item: ident, - })) - } else { - // We parsed the function ident as the interface ident - // Map the interface ident to the function ident - Ok(Some(Ident { - interface: None, - item: i.interface, - })) - } - } - Some(i) => { - // if we parse an interface id with a full package, we must - // be expecting a `#` next with the function ident - match input.pop_front() { - Some(t) if t.token() == TokenKind::Hash => { - let ident = Literal::parse_ident(input)?; - Ok(Some(Ident { - interface: Some(i), - item: ident, - })) - } - Some(t) => Err(ParserError::UnexpectedToken(t)), - None => Err(ParserError::UnexpectedEndOfInput), - } - } - - None => Ok(None), - } - } -} - -impl std::fmt::Display for Ident<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(interface) = self.interface { - write!(f, "{interface}#")? - } - write!(f, "{}", self.item) - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct InterfaceIdent<'a> { - package: Option<(&'a str, &'a str)>, - interface: &'a str, -} - -impl<'a> InterfaceIdent<'a> { - fn try_parse<'b>(input: &'b mut VecDeque>) -> Result, ParserError<'a>> { - #[derive(Debug)] - enum State<'a> { - ExpectFirst, - ExpectColon(&'a str), - ExpectSecond(&'a str), - ExpectSlash(&'a str, &'a str), - ExpectThird(&'a str, &'a str), - } - let mut state = State::ExpectFirst; - loop { - let token = input.front(); - match (token.map(|t| t.token()), state) { - (Some(TokenKind::Ident(i)), State::ExpectFirst) => { - input.pop_front(); - state = State::ExpectColon(i); - } - (Some(TokenKind::Colon), State::ExpectColon(first)) => { - input.pop_front(); - state = State::ExpectSecond(first); - } - (Some(TokenKind::Ident(second)), State::ExpectSecond(first)) => { - input.pop_front(); - state = State::ExpectSlash(first, second); - } - (Some(TokenKind::Slash), State::ExpectSlash(first, second)) => { - input.pop_front(); - state = State::ExpectThird(first, second); - } - (Some(TokenKind::Ident(third)), State::ExpectThird(first, second)) => { - input.pop_front(); - return Ok(Some(InterfaceIdent { - package: Some((first, second)), - interface: third, - })); - } - (_, State::ExpectColon(first)) => { - return Ok(Some(InterfaceIdent { - package: None, - interface: first, - })); - } - (_, State::ExpectFirst) => return Ok(None), - (Some(_), _) => return Err(ParserError::UnexpectedToken(*token.unwrap())), - _ => return Err(ParserError::UnexpectedEndOfInput), - } - } - } -} - -impl std::fmt::Display for InterfaceIdent<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some((namespace, package)) = self.package { - write!(f, "{namespace}:{package}/")?; - } - write!(f, "{}", self.interface) - } -} - -#[derive(Debug, PartialEq)] -pub enum Literal<'a> { - String(&'a str), - Number(usize), - List(List<'a>), - Record(Record<'a>), -} - -impl<'a> Literal<'a> { - fn parse_ident(input: &mut VecDeque>) -> Result<&'a str, ParserError<'a>> { - let Some(token) = input.front() else { - return Err(ParserError::UnexpectedEndOfInput); - }; - match token.token() { - TokenKind::Ident(i) => { - input.pop_front(); - Ok(i) - } - _ => Err(ParserError::UnexpectedToken(*token)), - } - } -} - -#[derive(Debug, PartialEq)] -pub struct List<'a> { - pub items: Vec>, -} - -impl<'a> From>> for List<'a> { - fn from(items: Vec>) -> Self { - Self { items } - } -} - -#[derive(Debug, PartialEq)] -pub struct Record<'a> { - pub fields: Vec<(&'a str, Expr<'a>)>, -} - -impl<'a> From)>> for Record<'a> { - fn from(fields: Vec<(&'a str, Expr<'a>)>) -> Self { - Self { fields } - } -} - -#[cfg(test)] -mod tests { - use std::vec; - - use crate::command::tokenizer::{SpannedStr, TokenKind}; - - use super::*; - - fn dummy_spanned_str() -> SpannedStr<'static> { - SpannedStr { str: "", offset: 0 } - } - - fn token(kind: TokenKind<'static>) -> Token<'static> { - Token { - input: dummy_spanned_str(), - token: kind, - } - } - - fn tokens(tokens: impl IntoIterator>) -> VecDeque> { - tokens.into_iter().map(token).collect() - } - - fn parse( - ts: impl IntoIterator>, - ) -> Result, ParserError<'static>> { - Line::parse(tokens(ts)) - } - - #[test] - fn parse_string_literals() { - let line = parse([TokenKind::String("hello-world")]).unwrap(); - assert_eq!( - line, - Line::Expr(Expr::Literal(Literal::String("hello-world"))) - ); - } - - #[test] - fn parse_list_literals() { - let list_of_string = Line::Expr(Expr::Literal(Literal::List( - vec![Expr::Literal(Literal::String("hello-world"))].into(), - ))); - let line = parse([ - TokenKind::OpenBracket, - TokenKind::String("hello-world"), - TokenKind::ClosedBracket, - ]) - .unwrap(); - assert_eq!(line, list_of_string); - - let line = parse([ - TokenKind::OpenBracket, - TokenKind::String("hello-world"), - TokenKind::Comma, - TokenKind::ClosedBracket, - ]) - .unwrap(); - assert_eq!(line, list_of_string); - - let err = parse([ - TokenKind::OpenBracket, - TokenKind::String("hello-world"), - TokenKind::Comma, - ]) - .unwrap_err(); - assert_eq!(err, ParserError::UnexpectedEndOfInput); - } - - #[test] - fn parse_record_literals() { - let record = Line::Expr(Expr::Literal(Literal::Record( - vec![("foo", Expr::Literal(Literal::String("bar")))].into(), - ))); - let line = parse([ - TokenKind::OpenBrace, - TokenKind::Ident("foo"), - TokenKind::Colon, - TokenKind::String("bar"), - TokenKind::ClosedBrace, - ]) - .unwrap(); - assert_eq!(line, record); - - let line = parse([ - TokenKind::OpenBrace, - TokenKind::Ident("foo"), - TokenKind::Colon, - TokenKind::String("bar"), - TokenKind::Comma, - TokenKind::ClosedBrace, - ]) - .unwrap(); - assert_eq!(line, record); - - let err = parse([ - TokenKind::OpenBrace, - TokenKind::Ident("foo"), - TokenKind::String("bar"), - TokenKind::ClosedBrace, - ]) - .unwrap_err(); - assert_eq!( - err, - ParserError::UnexpectedToken(token(TokenKind::String("bar"))) - ); - } - - #[test] - fn parse_function_calls() { - let function = Line::Expr(Expr::FunctionCall(FunctionCall { - ident: Ident { - interface: Some(InterfaceIdent { - package: Some(("foo", "bar")), - interface: "baz", - }), - item: "qux", - }, - args: vec![], - })); - let line = parse([ - TokenKind::Ident("foo"), - TokenKind::Colon, - TokenKind::Ident("bar"), - TokenKind::Slash, - TokenKind::Ident("baz"), - TokenKind::Hash, - TokenKind::Ident("qux"), - TokenKind::OpenParen, - TokenKind::ClosedParen, - ]) - .unwrap(); - assert_eq!(line, function); - - let function = Line::Expr(Expr::FunctionCall(FunctionCall { - ident: Ident { - interface: None, - item: "qux", - }, - args: vec![], - })); - let line = parse([ - TokenKind::Ident("qux"), - TokenKind::OpenParen, - TokenKind::ClosedParen, - ]) - .unwrap(); - assert_eq!(line, function); - - let function = Line::Expr(Expr::FunctionCall(FunctionCall { - ident: Ident { - interface: None, - item: "foo", - }, - args: vec![Expr::FunctionCall(FunctionCall { - ident: Ident { - interface: None, - item: "bar", - }, - args: vec![], - })], - })); - let line = parse([ - TokenKind::Ident("foo"), - TokenKind::OpenParen, - TokenKind::Ident("bar"), - TokenKind::OpenParen, - TokenKind::ClosedParen, - TokenKind::ClosedParen, - ]) - .unwrap(); - assert_eq!(line, function); - - let err = parse([ - TokenKind::Ident("foo"), - TokenKind::Colon, - TokenKind::Ident("bar"), - TokenKind::OpenParen, - TokenKind::ClosedParen, - ]) - .unwrap_err(); - assert_eq!( - err, - ParserError::UnexpectedToken(token(TokenKind::OpenParen)) - ); - - let err = parse([ - TokenKind::Ident("foo"), - TokenKind::Colon, - TokenKind::Ident("bar"), - TokenKind::Hash, - TokenKind::Ident("baz"), - TokenKind::OpenParen, - TokenKind::ClosedParen, - ]) - .unwrap_err(); - assert_eq!(err, ParserError::UnexpectedToken(token(TokenKind::Hash))); - } - - #[test] - fn parse_ident_expr() { - let line = parse([TokenKind::Ident("foo")]).unwrap(); - assert_eq!(line, Line::Expr(Expr::Ident("foo"))); - } - - #[test] - fn parse_builtin() { - let line = parse([TokenKind::Builtin("foo"), TokenKind::Ident("foo")]).unwrap(); - assert_eq!( - line, - Line::BuiltIn(BuiltIn { - name: "foo", - rest: vec![token(TokenKind::Ident("foo"))] - }) - ); - } - - #[test] - fn parse_assignment() { - let line = parse([ - TokenKind::Ident("foo"), - TokenKind::Equal, - TokenKind::String("bar"), - ]) - .unwrap(); - assert_eq!( - line, - Line::Assignment("foo", Expr::Literal(Literal::String("bar"))) - ); - } -} diff --git a/src/command/parser.rs b/src/command/parser.rs index 67001ef..f895906 100644 --- a/src/command/parser.rs +++ b/src/command/parser.rs @@ -1,168 +1,390 @@ -use std::ops::Range; +use std::collections::VecDeque; -use nom::branch::alt; -use nom::bytes::complete::tag; -use nom::character::complete::{alpha1, digit1, multispace0, multispace1}; -use nom::combinator::{cut, map, map_res, recognize}; -use nom::multi::{many0_count, separated_list0}; -use nom::sequence::{delimited, pair, preceded}; -use nom::InputTakeAtPosition; +use crate::command::tokenizer::TokenKind; + +use super::tokenizer::Token; #[derive(Debug, PartialEq)] pub enum Line<'a> { - Builtin(SpannedStr<'a>, Vec>), Expr(Expr<'a>), - Assignment(SpannedStr<'a>, Expr<'a>), + BuiltIn(BuiltIn<'a>), + Assignment(&'a str, Expr<'a>), } impl<'a> Line<'a> { - pub fn parse(input: &'a str) -> nom::IResult<&str, Line> { - let input = Span::new(input); - alt(( - map(builtin, |(name, args)| { - Line::Builtin(name.into(), args.into_iter().map(|s| s.into()).collect()) - }), - map(assignment, |(ident, expr)| { - Line::Assignment(ident.into(), expr) - }), - map(Expr::parse, Line::Expr), - ))(input) - .map_err(|e| e.map(|e| nom::error::Error::new(*e.input.fragment(), e.code))) - .map(|(rest, result)| (*rest.fragment(), result)) + pub fn parse(mut tokens: VecDeque>) -> Result, ParserError<'a>> { + let result = match BuiltIn::try_parse(&mut tokens)? { + Some(builtin) => Ok(Self::BuiltIn(builtin)), + None => match Self::try_parse_assignment(&mut tokens)? { + Some((ident, expr)) => Ok(Self::Assignment(ident, expr)), + None => match Expr::try_parse(&mut tokens)? { + Some(e) => Ok(Self::Expr(e)), + None => { + return match tokens.front() { + Some(t) => Err(ParserError::UnexpectedToken(*t)), + None => Err(ParserError::UnexpectedEndOfInput), + } + } + }, + }, + }; + if !tokens.is_empty() { + return Err(ParserError::RemainingInput); + } + result } -} -/// Used to collect span information during parsing -type Span<'a> = nom_locate::LocatedSpan<&'a str>; - -pub fn builtin(input: Span) -> nom::IResult)> { - alt((builtin_call, special_char))(input) + fn try_parse_assignment( + tokens: &mut VecDeque>, + ) -> Result)>, ParserError<'a>> { + let Some(token) = tokens.front() else { + return Ok(None); + }; + let TokenKind::Ident(ident) = token.token() else { + return Ok(None); + }; + let token = *token; + let _ = tokens.pop_front(); + if matches!(tokens.front().map(|t| t.token()), Some(TokenKind::Equal)) { + let _ = tokens.pop_front(); + match Expr::try_parse(tokens)? { + Some(e) => Ok(Some((ident, e))), + None => Err(ParserError::ExpectedExpr), + } + } else { + tokens.push_front(token); + Ok(None) + } + } } -pub fn builtin_call(input: Span) -> nom::IResult)> { - let (rest, _) = tag(".")(input)?; - let (rest, ident) = ident(rest)?; - if rest.is_empty() { - return Ok((rest, (ident, Vec::new()))); +#[derive(Debug, PartialEq)] +pub struct BuiltIn<'a> { + pub name: &'a str, + pub rest: Vec>, +} + +impl<'a> BuiltIn<'a> { + fn try_parse(tokens: &mut VecDeque>) -> Result>, ParserError<'a>> { + let Some(TokenKind::Builtin(ident)) = tokens.front().map(|t| t.token()) else { + return Ok(None); + }; + tokens.pop_front(); + Ok(Some(BuiltIn { + name: ident, + rest: tokens.drain(..).collect(), + })) } - let (rest, args) = separated_list0(multispace1, builtin_argument)(rest)?; +} - Ok((rest, (ident, args))) +#[derive(Debug, PartialEq)] +pub enum ParserError<'a> { + UnexpectedToken(Token<'a>), + UnexpectedEndOfInput, + RemainingInput, + ExpectedExpr, } -pub fn special_char(input: Span) -> nom::IResult)> { - let (rest, _) = tag("?")(input)?; - let (rest, _) = multispace0(rest)?; - let (rest, args) = separated_list0(multispace1, builtin_argument)(rest)?; - if args.is_empty() { - return Ok((rest, (Span::new("help"), Vec::new()))); +impl std::error::Error for ParserError<'_> {} + +impl std::fmt::Display for ParserError<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ParserError::UnexpectedToken(_) => f.write_str("unexpected token"), + ParserError::UnexpectedEndOfInput => f.write_str("unexpected end of input"), + ParserError::RemainingInput => f.write_str("remaining input"), + ParserError::ExpectedExpr => f.write_str("unexpected expression"), + } } - Ok((rest, (Span::new("inspect"), args))) } #[derive(Debug, PartialEq)] pub enum Expr<'a> { + FunctionCall(FunctionCall<'a>), + Ident(&'a str), Literal(Literal<'a>), - Ident(SpannedStr<'a>), - FunctionCall(FunctionIdent<'a>, Vec>), } impl<'a> Expr<'a> { - pub fn parse(input: Span) -> nom::IResult { - alt(( - map(function_call, |(name, args)| { - Expr::FunctionCall(name.into(), args) - }), - map(ident, |i| Expr::Ident(i.into())), - map(Literal::parse, Expr::Literal), - ))(input) + fn try_parse(input: &mut VecDeque>) -> Result>, ParserError<'a>> { + let Some(first) = input.front() else { + return Ok(None); + }; + match first.token() { + TokenKind::String(s) => { + input.pop_front(); + Ok(Some(Expr::Literal(Literal::String(s)))) + } + TokenKind::Number(n) => { + input.pop_front(); + Ok(Some(Expr::Literal(Literal::Number(n)))) + } + TokenKind::OpenBracket => { + input.pop_front(); + enum State { + ExpectExpr, + ExpectComma, + } + let mut state = State::ExpectExpr; + let mut items = vec![]; + while let Some(token) = input.front() { + match token.token() { + TokenKind::ClosedBracket => { + input.pop_front(); + return Ok(Some(Expr::Literal(Literal::List(List { items })))); + } + TokenKind::Comma if matches!(state, State::ExpectComma) => { + input.pop_front(); + state = State::ExpectExpr; + } + _ => { + let expr = Expr::try_parse(input)?; + if let Some(expr) = expr { + items.push(expr); + state = State::ExpectComma; + } else { + return Err(ParserError::UnexpectedEndOfInput); + } + } + } + } + return Err(ParserError::UnexpectedEndOfInput); + } + TokenKind::OpenBrace => { + input.pop_front(); + enum State<'a> { + ExpectIdent, + ExpectColon(&'a str), + ExpectExpr(&'a str), + ExpectComma, + } + let mut state = State::ExpectIdent; + let mut fields = vec![]; + while let Some(token) = input.front() { + match (token.token(), state) { + (TokenKind::ClosedBrace, State::ExpectComma | State::ExpectIdent) => { + input.pop_front(); + return Ok(Some(Expr::Literal(Literal::Record(Record { fields })))); + } + (TokenKind::Comma, State::ExpectComma) => { + input.pop_front(); + state = State::ExpectIdent; + } + (TokenKind::Colon, State::ExpectColon(ident)) => { + input.pop_front(); + state = State::ExpectExpr(ident); + } + (_, State::ExpectIdent) => { + let ident = Literal::parse_ident(input)?; + state = State::ExpectColon(ident); + } + (_, State::ExpectExpr(ident)) => { + let expr = Expr::try_parse(input)?; + if let Some(expr) = expr { + fields.push((ident, expr)); + state = State::ExpectComma; + } else { + return Err(ParserError::UnexpectedEndOfInput); + } + } + _ => return Err(ParserError::UnexpectedToken(*token)), + } + } + return Err(ParserError::UnexpectedEndOfInput); + } + TokenKind::Ident(_) => { + let func = FunctionCall::try_parse(input)?; + match func { + Some(f) => Ok(Some(Expr::FunctionCall(f))), + None => Ok(Some(Expr::Ident(Literal::parse_ident(input)?))), + } + } + + _ => return Ok(None), + } } } +#[derive(Debug, PartialEq)] +pub struct FunctionCall<'a> { + pub ident: ItemIdent<'a>, + pub args: Vec>, +} + +impl<'a> FunctionCall<'a> { + fn try_parse(input: &mut VecDeque>) -> Result, ParserError<'a>> { + let original = input.clone(); + let Some(function_ident) = ItemIdent::try_parse(input)? else { + return Ok(None); + }; + let next = input.front(); + if next.map(|t| t.token()) != Some(TokenKind::OpenParen) { + // If we failed to find an open paren then we need to completely bail + // on function parsing which means restoring the input state back to + // its original form. + *input = original; + return Ok(None); + } + expect_token(input, |t| t == TokenKind::OpenParen)?; + let mut args = Vec::new(); + loop { + let Some(expr) = Expr::try_parse(input)? else { + break; + }; + args.push(expr); + if input.front().map(|t| t.token()) != Some(TokenKind::Comma) { + break; + } + } + expect_token(input, |t| t == TokenKind::ClosedParen)?; + Ok(Some(FunctionCall { + ident: function_ident, + args, + })) + } +} + +fn expect_token<'a>( + input: &mut VecDeque>, + pred: impl FnOnce(TokenKind<'a>) -> bool, +) -> Result<(), ParserError<'a>> { + let Some(token) = input.pop_front() else { + return Err(ParserError::UnexpectedEndOfInput); + }; + if !pred(token.token()) { + return Err(ParserError::UnexpectedToken(token)); + } + Ok(()) +} + #[derive(Debug, PartialEq, Copy, Clone)] -pub enum ItemIdent<'a> { - Function(FunctionIdent<'a>), +pub enum Ident<'a> { + Item(ItemIdent<'a>), Interface(InterfaceIdent<'a>), } -impl<'a> ItemIdent<'a> { - pub fn parse(input: Span<'a>) -> nom::IResult, ItemIdent<'a>> { - alt(( - map(FunctionIdent::parse, |f| ItemIdent::Function(f)), - map(InterfaceIdent::parse, |i| ItemIdent::Interface(i)), - ))(input) +impl<'a> Ident<'a> { + pub(crate) fn try_parse( + input: &mut VecDeque>, + ) -> Result>, ParserError<'a>> { + match ItemIdent::try_parse(input)? { + Some(i) => Ok(Some(Self::Item(i))), + None => Ok(InterfaceIdent::try_parse(input)?.map(Self::Interface)), + } } } #[derive(Debug, PartialEq, Copy, Clone)] -pub struct FunctionIdent<'a> { +pub struct ItemIdent<'a> { pub interface: Option>, - pub function: SpannedStr<'a>, + pub item: &'a str, } -impl<'a> FunctionIdent<'a> { - pub fn parse(input: Span<'a>) -> nom::IResult, FunctionIdent<'a>> { - fn with_interface(input: Span<'_>) -> nom::IResult, FunctionIdent<'_>> { - let (rest, interface) = InterfaceIdent::parse(input)?; - let (rest, _) = tag("#")(rest)?; - let (rest, function) = cut(ident)(rest)?; - Ok(( - rest, - FunctionIdent { - interface: Some(interface), - function: function.into(), - }, - )) +impl<'a> ItemIdent<'a> { + fn try_parse(input: &mut VecDeque>) -> Result, ParserError<'a>> { + let interface = InterfaceIdent::try_parse(input)?; + match interface { + Some(i) if i.package.is_none() => { + if input.front().map(|t| t.token()) == Some(TokenKind::Hash) { + input.pop_front(); + let ident = Literal::parse_ident(input)?; + Ok(Some(ItemIdent { + interface: Some(i), + item: ident, + })) + } else { + // We parsed the function ident as the interface ident + // Map the interface ident to the function ident + Ok(Some(ItemIdent { + interface: None, + item: i.interface, + })) + } + } + Some(i) => { + // if we parse an interface id with a full package, we must + // be expecting a `#` next with the function ident + match input.pop_front() { + Some(t) if t.token() == TokenKind::Hash => { + let ident = Literal::parse_ident(input)?; + Ok(Some(ItemIdent { + interface: Some(i), + item: ident, + })) + } + Some(t) => Err(ParserError::UnexpectedToken(t)), + None => Err(ParserError::UnexpectedEndOfInput), + } + } + + None => Ok(None), } - alt(( - with_interface, - map(ident, |f| Self { - interface: None, - function: f.into(), - }), - ))(input) } } -impl std::fmt::Display for FunctionIdent<'_> { +impl std::fmt::Display for ItemIdent<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(interface) = self.interface { write!(f, "{interface}#")? } - write!(f, "{}", self.function) + write!(f, "{}", self.item) } } #[derive(Debug, PartialEq, Clone, Copy)] pub struct InterfaceIdent<'a> { - package: Option<(SpannedStr<'a>, SpannedStr<'a>)>, - interface: SpannedStr<'a>, + package: Option<(&'a str, &'a str)>, + interface: &'a str, } impl<'a> InterfaceIdent<'a> { - fn parse(input: Span<'a>) -> nom::IResult { - fn prefixed<'a>(input: Span<'a>) -> nom::IResult> { - let (rest, namespace) = ident(input)?; - let (rest, _) = tag(":")(rest)?; - let (rest, package) = cut(ident)(rest)?; - let (rest, _) = cut(tag("/"))(rest)?; - let (rest, interface) = cut(ident)(rest)?; - Ok(( - rest, - InterfaceIdent { - package: Some((namespace.into(), package.into())), - interface: interface.into(), - }, - )) + fn try_parse<'b>(input: &'b mut VecDeque>) -> Result, ParserError<'a>> { + #[derive(Debug)] + enum State<'a> { + ExpectFirst, + ExpectColon(&'a str), + ExpectSecond(&'a str), + ExpectSlash(&'a str, &'a str), + ExpectThird(&'a str, &'a str), + } + let mut state = State::ExpectFirst; + loop { + let token = input.front(); + match (token.map(|t| t.token()), state) { + (Some(TokenKind::Ident(i)), State::ExpectFirst) => { + input.pop_front(); + state = State::ExpectColon(i); + } + (Some(TokenKind::Colon), State::ExpectColon(first)) => { + input.pop_front(); + state = State::ExpectSecond(first); + } + (Some(TokenKind::Ident(second)), State::ExpectSecond(first)) => { + input.pop_front(); + state = State::ExpectSlash(first, second); + } + (Some(TokenKind::Slash), State::ExpectSlash(first, second)) => { + input.pop_front(); + state = State::ExpectThird(first, second); + } + (Some(TokenKind::Ident(third)), State::ExpectThird(first, second)) => { + input.pop_front(); + return Ok(Some(InterfaceIdent { + package: Some((first, second)), + interface: third, + })); + } + (_, State::ExpectColon(first)) => { + return Ok(Some(InterfaceIdent { + package: None, + interface: first, + })); + } + (_, State::ExpectFirst) => return Ok(None), + (Some(_), _) => return Err(ParserError::UnexpectedToken(*token.unwrap())), + _ => return Err(ParserError::UnexpectedEndOfInput), + } } - - alt(( - prefixed, - map(ident, |i| Self { - package: None, - interface: i.into(), - }), - ))(input) } } @@ -177,20 +399,24 @@ impl std::fmt::Display for InterfaceIdent<'_> { #[derive(Debug, PartialEq)] pub enum Literal<'a> { - Record(Record<'a>), + String(&'a str), + Number(usize), List(List<'a>), - String(SpannedStr<'a>), - Num(usize), + Record(Record<'a>), } impl<'a> Literal<'a> { - pub fn parse(input: Span) -> nom::IResult { - alt(( - map(number, Literal::Num), - map(Record::parse, Literal::Record), - map(List::parse, Literal::List), - map(string_literal, |s| Literal::String(s.into())), - ))(input) + fn parse_ident(input: &mut VecDeque>) -> Result<&'a str, ParserError<'a>> { + let Some(token) = input.front() else { + return Err(ParserError::UnexpectedEndOfInput); + }; + match token.token() { + TokenKind::Ident(i) => { + input.pop_front(); + Ok(i) + } + _ => Err(ParserError::UnexpectedToken(*token)), + } } } @@ -199,302 +425,251 @@ pub struct List<'a> { pub items: Vec>, } -impl<'a> List<'a> { - fn parse(input: Span<'a>) -> nom::IResult { - fn item(input: Span) -> nom::IResult> { - delimited(multispace0, Expr::parse, multispace0)(input) - } - let (rest, _) = tag("[")(input)?; - let (rest, items) = cut(separated_list0(tag(","), item))(rest)?; - let (rest, _) = cut(tag("]"))(rest)?; - Ok((rest, Self { items })) +impl<'a> From>> for List<'a> { + fn from(items: Vec>) -> Self { + Self { items } } } -fn number(input: Span) -> nom::IResult { - fn parse(input: Span) -> Result { - input.fragment().parse() - } - map_res(digit1, parse)(input) -} - #[derive(Debug, PartialEq)] pub struct Record<'a> { - pub fields: Vec<(SpannedStr<'a>, Expr<'a>)>, -} - -impl<'a> Record<'a> { - fn parse(input: Span<'a>) -> nom::IResult { - fn field(input: Span) -> nom::IResult)> { - let (rest, name) = preceded(multispace0, ident)(input)?; - let (rest, _) = tag(":")(rest)?; - let (rest, expr) = delimited(multispace0, Expr::parse, multispace0)(rest)?; - Ok((rest, (name, expr))) - } - let (rest, _) = tag("{")(input)?; - let (rest, fields) = cut(separated_list0(tag(","), field))(rest)?; - let fields = fields.into_iter().map(|(f, e)| (f.into(), e)).collect(); - let (rest, _) = cut(tag("}"))(rest)?; - Ok((rest, Self { fields })) - } -} - -fn assignment(input: Span) -> nom::IResult)> { - let (rest, ident) = ident(input)?; - let (rest, _) = delimited(multispace0, tag("="), multispace0)(rest)?; - let (r, value) = cut(Expr::parse)(rest)?; - Ok((r, (ident, value))) -} - -pub fn function_call(input: Span) -> nom::IResult, Vec>)> { - let (rest, ident) = FunctionIdent::parse(input)?; - let (rest, _) = tag("(")(rest)?; - let (rest, args) = cut(separated_list0(tag(","), Expr::parse))(rest)?; - let (rest, _) = cut(tag(")"))(rest)?; - - Ok((rest, (ident, args))) -} - -fn string_literal(input: Span) -> nom::IResult { - delimited(tag("\""), anything_but_quote, tag("\""))(input) -} - -fn builtin_argument(input: Span) -> nom::IResult { - alt(( - delimited(tag("\""), anything_but_quote, tag("\"")), - anything_but_space, - ))(input) -} - -fn anything_but_quote(input: Span) -> nom::IResult { - input.split_at_position_complete(|c| c == '"') -} - -/// Anything that is not whitespace -fn anything_but_space(input: Span) -> nom::IResult { - input.split_at_position_complete(char::is_whitespace) -} - -pub fn ident(input: Span) -> nom::IResult { - let ident_parser = recognize(pair(alpha1, many0_count(alt((alpha1, tag("-")))))); - delimited(multispace0, ident_parser, multispace0)(input) -} - -/// A view into the input str with span information -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct SpannedStr<'a> { - str: &'a str, - offset: usize, + pub fields: Vec<(&'a str, Expr<'a>)>, } -impl<'a> SpannedStr<'a> { - pub fn as_str(&self) -> &'a str { - self.str - } - - pub fn range(&self) -> Range { - self.offset..(self.offset + self.str.len()) +impl<'a> From)>> for Record<'a> { + fn from(fields: Vec<(&'a str, Expr<'a>)>) -> Self { + Self { fields } } } -impl<'a> PartialEq<&str> for SpannedStr<'a> { - fn eq(&self, other: &&str) -> bool { - self.str == *other - } -} - -impl std::fmt::Display for SpannedStr<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.str) - } -} +#[cfg(test)] +mod tests { + use std::vec; -impl From> for String { - fn from(value: SpannedStr<'_>) -> Self { - value.str.to_owned() - } -} + use crate::command::tokenizer::{SpannedStr, TokenKind}; -impl<'a> std::ops::Deref for SpannedStr<'a> { - type Target = str; + use super::*; - fn deref(&self) -> &Self::Target { - self.str + fn dummy_spanned_str() -> SpannedStr<'static> { + SpannedStr { str: "", offset: 0 } } -} -impl<'a> From> for SpannedStr<'a> { - fn from(span: Span<'a>) -> Self { - Self { - str: span.fragment(), - offset: span.location_offset(), + fn token(kind: TokenKind<'static>) -> Token<'static> { + Token { + input: dummy_spanned_str(), + token: kind, } } -} -impl<'a> From<(&'a str, usize)> for SpannedStr<'a> { - fn from((str, offset): (&'a str, usize)) -> Self { - Self { str, offset } + fn tokens(tokens: impl IntoIterator>) -> VecDeque> { + tokens.into_iter().map(token).collect() } -} -#[cfg(test)] -mod tests { - use super::*; + fn parse( + ts: impl IntoIterator>, + ) -> Result, ParserError<'static>> { + Line::parse(tokens(ts)) + } #[test] - fn function_call() { - let input = r#"my-func(my-other-func("arg"))"#; - let (rest, result) = Line::parse(input).unwrap(); - assert!(rest.is_empty()); + fn parse_string_literals() { + let line = parse([TokenKind::String("hello-world")]).unwrap(); assert_eq!( - result, - Line::Expr(Expr::FunctionCall( - FunctionIdent { - interface: None, - function: ("my-func", 0).into() - }, - vec![Expr::FunctionCall( - FunctionIdent { - interface: None, - function: ("my-other-func", 8).into() - }, - vec![Expr::Literal(Literal::String(("arg", 23).into()))] - )] - )) + line, + Line::Expr(Expr::Literal(Literal::String("hello-world"))) ); } #[test] - fn namespaced_interface_function_call() { - let input = r#"wasi:cli/terminal-stderr#my-func()"#; - let (rest, result) = Line::parse(input).unwrap(); - assert!(rest.is_empty()); - assert_eq!( - result, - Line::Expr(Expr::FunctionCall( - FunctionIdent { - interface: Some(InterfaceIdent { - package: Some((("wasi", 0).into(), ("cli", 5).into())), - interface: ("terminal-stderr", 9).into() - }), - function: ("my-func", 25).into() - }, - vec![] - )) - ); + fn parse_list_literals() { + let list_of_string = Line::Expr(Expr::Literal(Literal::List( + vec![Expr::Literal(Literal::String("hello-world"))].into(), + ))); + let line = parse([ + TokenKind::OpenBracket, + TokenKind::String("hello-world"), + TokenKind::ClosedBracket, + ]) + .unwrap(); + assert_eq!(line, list_of_string); + + let line = parse([ + TokenKind::OpenBracket, + TokenKind::String("hello-world"), + TokenKind::Comma, + TokenKind::ClosedBracket, + ]) + .unwrap(); + assert_eq!(line, list_of_string); + + let err = parse([ + TokenKind::OpenBracket, + TokenKind::String("hello-world"), + TokenKind::Comma, + ]) + .unwrap_err(); + assert_eq!(err, ParserError::UnexpectedEndOfInput); } #[test] - fn interface_function_call() { - let input = r#"terminal-stderr#my-func()"#; - let (rest, result) = Line::parse(input).unwrap(); - assert!(rest.is_empty()); + fn parse_record_literals() { + let record = Line::Expr(Expr::Literal(Literal::Record( + vec![("foo", Expr::Literal(Literal::String("bar")))].into(), + ))); + let line = parse([ + TokenKind::OpenBrace, + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::String("bar"), + TokenKind::ClosedBrace, + ]) + .unwrap(); + assert_eq!(line, record); + + let line = parse([ + TokenKind::OpenBrace, + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::String("bar"), + TokenKind::Comma, + TokenKind::ClosedBrace, + ]) + .unwrap(); + assert_eq!(line, record); + + let err = parse([ + TokenKind::OpenBrace, + TokenKind::Ident("foo"), + TokenKind::String("bar"), + TokenKind::ClosedBrace, + ]) + .unwrap_err(); assert_eq!( - result, - Line::Expr(Expr::FunctionCall( - FunctionIdent { - interface: Some(InterfaceIdent { - package: None, - interface: ("terminal-stderr", 0).into() - }), - function: ("my-func", 16).into() - }, - vec![] - )) + err, + ParserError::UnexpectedToken(token(TokenKind::String("bar"))) ); } #[test] - fn function_call_bad_args() { - let input = r#"my-func(%^&)"#; - let result = Line::parse(input); - assert!(matches!(result, Err(nom::Err::Failure(_)))); - } - - #[test] - fn function_call_with_record() { - let input = r#"my-func({n:1, name: err("string") })"#; - let (rest, result) = Line::parse(input).unwrap(); - assert!(rest.is_empty()); - - assert_eq!( - result, - Line::Expr(Expr::FunctionCall( - FunctionIdent { + fn parse_function_calls() { + let function = Line::Expr(Expr::FunctionCall(FunctionCall { + ident: ItemIdent { + interface: Some(InterfaceIdent { + package: Some(("foo", "bar")), + interface: "baz", + }), + item: "qux", + }, + args: vec![], + })); + let line = parse([ + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::Ident("bar"), + TokenKind::Slash, + TokenKind::Ident("baz"), + TokenKind::Hash, + TokenKind::Ident("qux"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + ]) + .unwrap(); + assert_eq!(line, function); + + let function = Line::Expr(Expr::FunctionCall(FunctionCall { + ident: ItemIdent { + interface: None, + item: "qux", + }, + args: vec![], + })); + let line = parse([ + TokenKind::Ident("qux"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + ]) + .unwrap(); + assert_eq!(line, function); + + let function = Line::Expr(Expr::FunctionCall(FunctionCall { + ident: ItemIdent { + interface: None, + item: "foo", + }, + args: vec![Expr::FunctionCall(FunctionCall { + ident: ItemIdent { interface: None, - function: ("my-func", 0).into() + item: "bar", }, - vec![Expr::Literal(Literal::Record(Record { - fields: vec![ - (("n", 9).into(), Expr::Literal(Literal::Num(1))), - ( - ("name", 15).into(), - Expr::FunctionCall( - FunctionIdent { - interface: None, - function: ("err", 24).into() - }, - vec![Expr::Literal(Literal::String(("string", 29).into()))] - ) - ) - ] - }))] - )) + args: vec![], + })], + })); + let line = parse([ + TokenKind::Ident("foo"), + TokenKind::OpenParen, + TokenKind::Ident("bar"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + TokenKind::ClosedParen, + ]) + .unwrap(); + assert_eq!(line, function); + + let err = parse([ + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::Ident("bar"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + ]) + .unwrap_err(); + assert_eq!( + err, + ParserError::UnexpectedToken(token(TokenKind::OpenParen)) ); + + let err = parse([ + TokenKind::Ident("foo"), + TokenKind::Colon, + TokenKind::Ident("bar"), + TokenKind::Hash, + TokenKind::Ident("baz"), + TokenKind::OpenParen, + TokenKind::ClosedParen, + ]) + .unwrap_err(); + assert_eq!(err, ParserError::UnexpectedToken(token(TokenKind::Hash))); } #[test] - fn builtin() { - let input = r#".foo bar baz"#; - let (rest, result) = Line::parse(input).unwrap(); - assert!(rest.is_empty()); - assert_eq!( - result, - Line::Builtin( - ("foo", 1).into(), - vec![("bar", 5).into(), ("baz", 9).into(),] - ) - ); + fn parse_ident_expr() { + let line = parse([TokenKind::Ident("foo")]).unwrap(); + assert_eq!(line, Line::Expr(Expr::Ident("foo"))); } #[test] - fn builtin_no_args() { - let input = r#".foo"#; - let result = Line::parse(input); + fn parse_builtin() { + let line = parse([TokenKind::Builtin("foo"), TokenKind::Ident("foo")]).unwrap(); assert_eq!( - result, - Ok(("".into(), Line::Builtin(("foo", 1).into(), vec![]))) + line, + Line::BuiltIn(BuiltIn { + name: "foo", + rest: vec![token(TokenKind::Ident("foo"))] + }) ); } #[test] - fn assignment() { - let input = r#"x = "wow""#; - let (rest, result) = Line::parse(input).unwrap(); - assert!(rest.is_empty()); - + fn parse_assignment() { + let line = parse([ + TokenKind::Ident("foo"), + TokenKind::Equal, + TokenKind::String("bar"), + ]) + .unwrap(); assert_eq!( - result, - Line::Assignment( - ("x", 0).into(), - Expr::Literal(Literal::String(("wow", 5).into())) - ) + line, + Line::Assignment("foo", Expr::Literal(Literal::String("bar"))) ); } - - #[test] - fn ident_line() { - let input = r#"hello-world"#; - let (rest, result) = Line::parse(input).unwrap(); - assert!(rest.is_empty()); - assert_eq!(result, Line::Expr(Expr::Ident(("hello-world", 0).into()))); - } - - #[test] - fn nonsense_assignment() { - let input = r#"x = %&*"#; - let result = Line::parse(input); - assert!(matches!(result, Err(nom::Err::Failure(_)))); - } } diff --git a/src/command/tokenizer.rs b/src/command/tokenizer.rs index f4f6e44..435e802 100644 --- a/src/command/tokenizer.rs +++ b/src/command/tokenizer.rs @@ -54,7 +54,7 @@ impl<'a> Token<'a> { let mut chars = rest.chars().peekable(); let original_offset = rest.offset; let Some(first) = chars.next() else { - panic!("TODO") + return Ok((rest, None)); }; let (offset, token_kind) = match first { '"' => { @@ -84,7 +84,9 @@ impl<'a> Token<'a> { .map(|c| c.len_utf8()) .sum(); let offset = c.len_utf8() + len; - let num = rest.str[..offset].parse().expect("TODO"); + let num = rest.str[..offset] + .parse() + .expect("failed to parse ascii digits as number"); (offset, Some(TokenKind::Number(num))) } c if c.is_whitespace() => (c.len_utf8(), None), diff --git a/src/evaluator.rs b/src/evaluator.rs index 58eab1a..12a28a7 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -3,11 +3,11 @@ use std::collections::HashMap; use anyhow::{bail, Context}; use wasmtime::component::{self, List, Record, Val}; -use crate::{command::alt_parser, runtime::Runtime, wit::WorldResolver}; +use crate::{command::parser, runtime::Runtime, wit::WorldResolver}; pub struct Evaluator<'a> { runtime: &'a mut Runtime, - querier: &'a WorldResolver, + resolver: &'a WorldResolver, scope: &'a HashMap, } @@ -15,12 +15,12 @@ impl<'a> Evaluator<'a> { /// Create a new evaluator pub fn new( runtime: &'a mut Runtime, - querier: &'a WorldResolver, + resolver: &'a WorldResolver, scope: &'a HashMap, ) -> Self { Self { runtime, - querier, + resolver, scope, } } @@ -28,13 +28,13 @@ impl<'a> Evaluator<'a> { /// Evaluate the expression with the provided type hint pub fn eval( &mut self, - expr: alt_parser::Expr<'_>, + expr: parser::Expr<'_>, type_hint: Option<&component::Type>, ) -> anyhow::Result { match expr { - alt_parser::Expr::Literal(l) => self.eval_literal(l, type_hint), - alt_parser::Expr::Ident(ident) => self.resolve_ident(&*ident, type_hint), - alt_parser::Expr::FunctionCall(func) => { + parser::Expr::Literal(l) => self.eval_literal(l, type_hint), + parser::Expr::Ident(ident) => self.resolve_ident(&*ident, type_hint), + parser::Expr::FunctionCall(func) => { let ident = func.ident; let mut args = func.args; log::debug!( @@ -81,12 +81,12 @@ impl<'a> Evaluator<'a> { /// Call the function pub fn call_func( &mut self, - ident: alt_parser::Ident, - args: Vec>, + ident: parser::ItemIdent, + args: Vec>, ) -> anyhow::Result> { log::debug!("Calling function: {ident} with args: {args:?}"); let func_def = self - .querier + .resolver .exported_function(ident) .with_context(|| format!("no function with name '{ident}'"))?; let mut evaled_args = Vec::with_capacity(func_def.params.len()); @@ -115,11 +115,11 @@ impl<'a> Evaluator<'a> { /// Evaluate a literal using the provided type hint pub fn eval_literal( &mut self, - literal: alt_parser::Literal<'_>, + literal: parser::Literal<'_>, type_hint: Option<&component::Type>, ) -> anyhow::Result { match literal { - alt_parser::Literal::List(list) => { + parser::Literal::List(list) => { match type_hint { Some(component::Type::List(l)) => { let mut values = Vec::new(); @@ -154,7 +154,7 @@ impl<'a> Evaluator<'a> { } } } - alt_parser::Literal::Record(mut r) => { + parser::Literal::Record(mut r) => { let ty = match type_hint { Some(component::Type::Record(r)) => r, Some(t) => bail!( @@ -181,7 +181,7 @@ impl<'a> Evaluator<'a> { } Ok(Val::Record(Record::new(ty, values)?)) } - alt_parser::Literal::String(s) => { + parser::Literal::String(s) => { let val = Val::String(s.to_owned().into()); match type_hint { Some(component::Type::Result(r)) => r.new_val(match (r.ok(), r.err()) { @@ -192,7 +192,7 @@ impl<'a> Evaluator<'a> { _ => Ok(val), } } - alt_parser::Literal::Number(n) => match type_hint { + parser::Literal::Number(n) => match type_hint { Some(component::Type::U8) => Ok(Val::U8(n.try_into()?)), _ => Ok(Val::S32(n.try_into()?)), }, diff --git a/src/main.rs b/src/main.rs index 49fcba3..4ae1969 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,8 +28,8 @@ fn _main() -> anyhow::Result<()> { let cli = Cli::parse(); let component_bytes = std::fs::read(cli.component)?; - let mut querier = wit::WorldResolver::from_bytes(&component_bytes)?; - let mut runtime = runtime::Runtime::init(component_bytes, &querier, |import_name| { + let mut resolver = wit::WorldResolver::from_bytes(&component_bytes)?; + let mut runtime = runtime::Runtime::init(component_bytes, &resolver, |import_name| { print_error_prefix(); eprintln!("unimplemented import: {import_name}"); })?; @@ -38,7 +38,7 @@ fn _main() -> anyhow::Result<()> { if let Some(home) = home::home_dir() { let _ = rl.load_history(&home.join(".weplhistory")); } - let world = querier.world_name(); + let world = resolver.world_name(); println!("{}: {world}", "World".blue().bold()); let mut scope = HashMap::default(); let prompt = "> ".blue().bold().to_string(); @@ -50,7 +50,7 @@ fn _main() -> anyhow::Result<()> { let line = command::Cmd::parse(&line); match line { Ok(Some(cmd)) => { - match cmd.run(&mut runtime, &mut querier, &mut scope) { + match cmd.run(&mut runtime, &mut resolver, &mut scope) { Err(e) => { print_error_prefix(); eprintln!("{e}"); diff --git a/src/runtime.rs b/src/runtime.rs index 3ee552f..b870bae 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -15,7 +15,7 @@ use wasmtime_wasi::preview2::{ }; use crate::{ - command::alt_parser::{self, Ident}, + command::parser::{self, ItemIdent}, wit::WorldResolver, }; @@ -31,7 +31,7 @@ pub struct Runtime { impl Runtime { pub fn init( component_bytes: Vec, - querier: &WorldResolver, + resolver: &WorldResolver, stub_import: impl Fn(&str) + Sync + Send + Clone + 'static, ) -> anyhow::Result { let engine = load_engine()?; @@ -39,13 +39,13 @@ impl Runtime { let mut linker = Linker::::new(&engine); linker.allow_shadowing(true); - let imports_wasi_cli = querier.imports_wasi_cli(); + let imports_wasi_cli = resolver.imports_wasi_cli(); if imports_wasi_cli { log::debug!("Linking with wasi"); wasmtime_wasi::preview2::command::sync::add_to_linker(&mut linker)?; } - for (import_name, import) in querier.imports(!imports_wasi_cli) { - let import_name = querier.world_item_name(import_name); + for (import_name, import) in resolver.imports(!imports_wasi_cli) { + let import_name = resolver.world_item_name(import_name); let stub_import = stub_import.clone(); match import { wit_parser::WorldItem::Function(f) => { @@ -57,7 +57,7 @@ impl Runtime { })?; } wit_parser::WorldItem::Interface(i) => { - let interface = querier.interface_by_id(*i).unwrap(); + let interface = resolver.interface_by_id(*i).unwrap(); let mut root = linker.root(); let mut instance = root.instance(&import_name)?; for (_, f) in interface.functions.iter() { @@ -69,7 +69,7 @@ impl Runtime { })?; } for (name, t) in &interface.types { - let t = querier.type_by_id(*t).unwrap(); + let t = resolver.type_by_id(*t).unwrap(); match &t.kind { wit_parser::TypeDefKind::Resource => { let ty = wasmtime::component::ResourceType::host::<()>(); @@ -98,7 +98,7 @@ impl Runtime { }) } - pub fn get_func(&mut self, ident: Ident) -> anyhow::Result { + pub fn get_func(&mut self, ident: ItemIdent) -> anyhow::Result { let func = match ident.interface { Some(i) => { let mut exports = self.instance.exports(&mut self.store); @@ -137,24 +137,22 @@ impl Runtime { /// export needed. pub fn stub( &mut self, - querier: &WorldResolver, - import_ident: alt_parser::ItemIdent<'_>, - export_ident: alt_parser::ItemIdent<'_>, + resolver: &WorldResolver, + import_ident: parser::Ident<'_>, + export_ident: parser::Ident<'_>, component_bytes: &[u8], ) -> anyhow::Result<()> { match (import_ident, export_ident) { - ( - alt_parser::ItemIdent::Item(import_ident), - alt_parser::ItemIdent::Item(export_ident), - ) => self.stub_function(querier, import_ident, export_ident, component_bytes), - ( - alt_parser::ItemIdent::Interface(import_ident), - alt_parser::ItemIdent::Interface(export_ident), - ) => self.stub_interface(querier, import_ident, export_ident, component_bytes), - (alt_parser::ItemIdent::Interface(_), alt_parser::ItemIdent::Item(_)) => { + (parser::Ident::Item(import_ident), parser::Ident::Item(export_ident)) => { + self.stub_function(resolver, import_ident, export_ident, component_bytes) + } + (parser::Ident::Interface(import_ident), parser::Ident::Interface(export_ident)) => { + self.stub_interface(resolver, import_ident, export_ident, component_bytes) + } + (parser::Ident::Interface(_), parser::Ident::Item(_)) => { anyhow::bail!("cannot satisfy interface import with a function") } - (alt_parser::ItemIdent::Item(_), alt_parser::ItemIdent::Interface(_)) => { + (parser::Ident::Item(_), parser::Ident::Interface(_)) => { anyhow::bail!("cannot satisfy function import with an interface") } } @@ -162,9 +160,9 @@ impl Runtime { pub fn stub_interface( &mut self, - querier: &WorldResolver, - import_ident: alt_parser::InterfaceIdent<'_>, - export_ident: alt_parser::InterfaceIdent<'_>, + resolver: &WorldResolver, + import_ident: parser::InterfaceIdent<'_>, + export_ident: parser::InterfaceIdent<'_>, component_bytes: &[u8], ) -> anyhow::Result<()> { let component = load_component(&self.engine, component_bytes)?; @@ -174,7 +172,7 @@ impl Runtime { let mut import_instance = root .instance(&import_ident.to_string()) .with_context(|| format!("no imported instance named '{import_ident}' found"))?; - let import = querier + let import = resolver .imported_interface(import_ident) .with_context(|| format!("no imported interface named '{import_ident}' found"))?; let other = WorldResolver::from_bytes(component_bytes)?; @@ -197,7 +195,7 @@ impl Runtime { .iter() .zip(&exported_function.params) { - if !types_equal(querier, p1, &other, p2) { + if !types_equal(resolver, p1, &other, p2) { anyhow::bail!( "different types for arg '{arg_name}' in function '{fun_name}'" ) @@ -214,13 +212,13 @@ impl Runtime { .collect::>(); for (name, ty) in is { let e = es.get(name).with_context(|| format!("exported function '{fun_name}' does not have return value '{name}'"))?; - if !types_equal(querier, ty, &other, e) { + if !types_equal(resolver, ty, &other, e) { anyhow::bail!("return value '{name}' has differing types"); } } } (wit_parser::Results::Anon(t1), wit_parser::Results::Anon(t2)) => { - if !types_equal(querier, t1, &other, t2) { + if !types_equal(resolver, t1, &other, t2) { anyhow::bail!("return types did not match for function {fun_name}"); } } @@ -257,13 +255,13 @@ impl Runtime { pub fn stub_function( &mut self, - querier: &WorldResolver, - import_ident: alt_parser::Ident<'_>, - export_ident: alt_parser::Ident<'_>, + resolver: &WorldResolver, + import_ident: parser::ItemIdent<'_>, + export_ident: parser::ItemIdent<'_>, component_bytes: &[u8], ) -> anyhow::Result<()> { // type checking - let import = querier + let import = resolver .imported_function(import_ident) .with_context(|| format!("no import with name '{import_ident}'"))?; let other = WorldResolver::from_bytes(component_bytes)?; @@ -504,29 +502,29 @@ impl WasiView for ImportImplsContext { } fn types_equal( - querier1: &WorldResolver, + resolver1: &WorldResolver, t1: &wit_parser::Type, - querier2: &WorldResolver, + resolver2: &WorldResolver, t2: &wit_parser::Type, ) -> bool { match (t1, t2) { (wit_parser::Type::Id(t1), wit_parser::Type::Id(t2)) => { - let t1 = querier1.type_by_id(*t1).unwrap(); - let t2 = querier2.type_by_id(*t2).unwrap(); - type_defs_equal(querier1, &t1.kind, querier2, &t2.kind) + let t1 = resolver1.type_by_id(*t1).unwrap(); + let t2 = resolver2.type_by_id(*t2).unwrap(); + type_defs_equal(resolver1, &t1.kind, resolver2, &t2.kind) } (wit_parser::Type::Id(t1), t2) => { - let t1 = querier1.type_by_id(*t1).unwrap(); + let t1 = resolver1.type_by_id(*t1).unwrap(); if let wit_parser::TypeDefKind::Type(t1) = &t1.kind { - types_equal(querier1, t1, querier2, t2) + types_equal(resolver1, t1, resolver2, t2) } else { false } } (t1, wit_parser::Type::Id(t2)) => { - let t2 = querier1.type_by_id(*t2).unwrap(); + let t2 = resolver1.type_by_id(*t2).unwrap(); if let wit_parser::TypeDefKind::Type(t2) = &t2.kind { - types_equal(querier1, t1, querier2, t2) + types_equal(resolver1, t1, resolver2, t2) } else { false } @@ -536,27 +534,27 @@ fn types_equal( } fn type_defs_equal( - querier1: &WorldResolver, + resolver1: &WorldResolver, t1: &wit_parser::TypeDefKind, - querier2: &WorldResolver, + resolver2: &WorldResolver, t2: &wit_parser::TypeDefKind, ) -> bool { match (t1, t2) { (wit_parser::TypeDefKind::Result(r1), wit_parser::TypeDefKind::Result(r2)) => { let oks = match (&r1.ok, &r2.ok) { (None, None) => true, - (Some(t1), Some(t2)) => types_equal(querier1, t1, querier2, t2), + (Some(t1), Some(t2)) => types_equal(resolver1, t1, resolver2, t2), _ => false, }; let errs = match (&r1.err, &r2.err) { (None, None) => true, - (Some(t1), Some(t2)) => types_equal(querier1, t1, querier2, t2), + (Some(t1), Some(t2)) => types_equal(resolver1, t1, resolver2, t2), _ => false, }; oks && errs } (wit_parser::TypeDefKind::List(t1), wit_parser::TypeDefKind::List(t2)) => { - types_equal(querier1, t1, querier2, t2) + types_equal(resolver1, t1, resolver2, t2) } (wit_parser::TypeDefKind::Variant(v1), wit_parser::TypeDefKind::Variant(v2)) => { if v1.cases.len() != v2.cases.len() { @@ -564,7 +562,7 @@ fn type_defs_equal( } v1.cases.iter().zip(v2.cases.iter()).all(|(c1, c2)| { let types_equal = match (&c1.ty, &c2.ty) { - (Some(t1), Some(t2)) => types_equal(querier1, t1, querier2, t2), + (Some(t1), Some(t2)) => types_equal(resolver1, t1, resolver2, t2), (None, None) => true, _ => false, }; diff --git a/src/wit.rs b/src/wit.rs index b79b511..79b5b78 100644 --- a/src/wit.rs +++ b/src/wit.rs @@ -7,7 +7,7 @@ use wit_parser::{ WorldKey, }; -use crate::command::alt_parser; +use crate::command::parser; /// A resolver for a wit world. pub struct WorldResolver { @@ -38,7 +38,7 @@ impl WorldResolver { } /// Get the exported function by the given `FunctionIdent`. - pub fn exported_function(&self, ident: alt_parser::Ident) -> Option<&Function> { + pub fn exported_function(&self, ident: parser::ItemIdent) -> Option<&Function> { match ident.interface { Some(i) => { let interface = self.exported_interface(i)?; @@ -55,7 +55,7 @@ impl WorldResolver { } /// Get the imported function by the given `FunctionIdent`. - pub fn imported_function(&self, ident: alt_parser::Ident) -> Option<&Function> { + pub fn imported_function(&self, ident: parser::ItemIdent) -> Option<&Function> { match ident.interface { Some(i) => { let interface = self.imported_interface(i)?; @@ -72,12 +72,12 @@ impl WorldResolver { } /// Get the exported interface by the given `InterfaceIdent`. - pub fn exported_interface(&self, ident: alt_parser::InterfaceIdent) -> Option<&Interface> { + pub fn exported_interface(&self, ident: parser::InterfaceIdent) -> Option<&Interface> { self.interface_in_items(ident, self.world().exports.iter()) } /// Get the imported interface by the given `InterfaceIdent`. - pub fn imported_interface(&self, ident: alt_parser::InterfaceIdent) -> Option<&Interface> { + pub fn imported_interface(&self, ident: parser::InterfaceIdent) -> Option<&Interface> { self.interface_in_items(ident, self.world().imports.iter()) } @@ -342,7 +342,7 @@ impl WorldResolver { /// Get an interface by its ident from a list of world items. fn interface_in_items<'a>( &self, - ident: alt_parser::InterfaceIdent, + ident: parser::InterfaceIdent, items: impl Iterator, ) -> Option<&Interface> { let item = self.get_world_item_by_name(items, &ident.to_string())?;