#![allow(unused)]
macro_rules! span {
($source_id:expr, $at:expr) => {
SourceSpan::at($source_id, $at)
};
($source_id:expr, $start:expr, $end:expr) => {
SourceSpan::new($source_id, ($start)..($end))
};
}
macro_rules! spanned {
($source_id:expr, $at:expr, $value:expr) => {
Span::new(span!($source_id, $at), $value)
};
($source_id:expr, $start:expr, $end:expr, $value:expr) => {
Span::new(span!($source_id, $start, $end), $value)
};
}
mod asm_parser;
mod delimiter;
mod error;
mod from_str_radix;
pub mod lexer;
mod operation;
mod parser;
pub mod scanner;
#[cfg(test)]
mod tests;
mod token;
use alloc::{
boxed::Box,
format,
rc::Rc,
string::{String, ToString},
sync::Arc,
vec,
vec::Vec,
};
use core::{
num::NonZeroU8,
ops::{Deref, DerefMut},
};
use miden_core::Felt;
use midenc_hir_type::{AddressSpace, PointerType};
use midenc_session::{
SourceManager,
diagnostics::{
ColumnNumber, Diagnostic, FileLineCol, LabeledSpan, LineNumber, RelatedError, Report,
Severity, SourceFile, SourceId, SourceManagerExt, SourceSpan, Span, Uri, WrapErr,
miette::{self, diagnostic},
},
};
use self::{
asm_parser::AsmParserState,
lexer::{Lexed, Lexer, TokenStream},
parser::DefaultParser,
scanner::Scanner,
};
pub use self::{
delimiter::Delimiter,
error::ParserError,
from_str_radix::FromStrRadix,
parser::{Parser, ParserExt},
token::Token,
};
use super::{
AsCallableSymbolRef, BlockArgumentRef, BlockId, BlockRef, Context, GenericOperationBuilder,
Ident, OpBuilder, OpRegistration, OpResultRef, Operation, OperationName, OperationRef,
OperationState, RawEntityRef, RegionRef, SuccessorInfo, SymbolRef, Type,
UnsafeIntrusiveEntityRef, ValueId, ValueRef, interner::Symbol, operation::ParseAssemblyFn,
};
use crate::{
Attribute, AttributeRef, Builder, CallableOpInterface, CompactString, FunctionType, FxHashMap,
NamedAttribute, ProgramPoint, SmallVec, ToCompactString,
adt::{SmallOrdMap, smallmap::SmallMap},
dialects::builtin::{
WorldBuilder, WorldRef,
attributes::{Location, LocationAttr},
},
formatter::DisplayValues,
interner,
print::TypePrinter,
smallvec,
};
pub trait OpParser {
fn parse(state: &mut OperationState, parser: &mut dyn OpAsmParser<'_>) -> ParseResult;
}
pub type ParseResult<T = ()> = Result<T, ParserError>;
pub struct ParserConfig {
pub context: Rc<Context>,
pub verify: bool,
}
impl ParserConfig {
pub fn new(context: Rc<Context>) -> Self {
Self {
context,
verify: true,
}
}
pub fn verify_after_parse(mut self, yes: bool) -> Self {
self.verify = yes;
self
}
#[inline]
pub const fn should_verify_after_parse(&self) -> bool {
self.verify
}
}
pub struct ParserState<'input> {
pub config: ParserConfig,
pub token_stream: TokenStream<'input>,
pub symbols: SymbolState,
pub asm_state: Option<Box<AsmParserState>>,
pub default_dialect_stack: SmallVec<[interner::Symbol; 1]>,
}
impl<'input> ParserState<'input> {
pub fn new(config: ParserConfig, token_stream: TokenStream<'input>) -> Self {
Self {
config,
token_stream,
symbols: Default::default(),
asm_state: None,
default_dialect_stack: smallvec![interner::Symbol::intern("builtin")],
}
}
#[inline]
pub fn context(&self) -> &Context {
&self.config.context
}
#[inline]
pub fn context_rc(&self) -> Rc<Context> {
self.config.context.clone()
}
}
#[derive(Default)]
pub struct SymbolState {
pub attribute_alias_definitions: FxHashMap<interner::Symbol, Span<AttributeRef>>,
pub type_alias_definitions: FxHashMap<interner::Symbol, Span<Type>>,
}
pub fn parse_generic(
config: ParserConfig,
uri: Uri,
source: impl Into<String>,
) -> Result<WorldRef, Report> {
use midenc_session::diagnostics::SourceLanguage;
let source_manager = &config.context.session().source_manager;
let source_file = source_manager.load(SourceLanguage::Other("hir"), uri, source.into());
parse_source_generic(config, source_file)
}
#[cfg(feature = "std")]
pub fn parse_file_generic(
config: ParserConfig,
path: impl AsRef<std::path::Path>,
) -> Result<WorldRef, Report> {
let source_manager = &config.context.session().source_manager;
let source_file = source_manager.load_file(path.as_ref()).map_err(Report::msg)?;
parse_source_generic(config, source_file)
}
fn parse_source_generic(
config: ParserConfig,
source_file: Arc<SourceFile>,
) -> Result<WorldRef, Report> {
let source = source_file.as_str();
let scanner = Scanner::new(source);
let token_stream = TokenStream::new(source_file.id(), scanner);
let mut parser = DefaultParser::new(ParserState::new(config, token_stream));
parser.state_mut().asm_state = Some(Default::default());
let span = parser.current_location();
let mut operation_parser = operation::TopLevelOperationParser::new(parser);
operation_parser
.parse(span)
.map_err(|err| Report::from(err).with_source_code(source_file.clone()))
}
pub fn parse<T: OpParser + OpRegistration>(
config: ParserConfig,
uri: Uri,
source: impl Into<String>,
) -> Result<UnsafeIntrusiveEntityRef<T>, Report> {
use midenc_session::diagnostics::SourceLanguage;
let source_manager = &config.context.session().source_manager;
let source_file = source_manager.load(SourceLanguage::Other("hir"), uri, source.into());
parse_source(config, source_file)
}
#[cfg(feature = "std")]
pub fn parse_file<T: OpParser + OpRegistration>(
config: ParserConfig,
path: impl AsRef<std::path::Path>,
) -> Result<UnsafeIntrusiveEntityRef<T>, Report> {
let source_manager = &config.context.session().source_manager;
let source_file = source_manager.load_file(path.as_ref()).map_err(Report::msg)?;
parse_source(config, source_file)
}
pub fn parse_any(
config: ParserConfig,
uri: Uri,
source: impl Into<String>,
) -> Result<OperationRef, Report> {
use midenc_session::diagnostics::SourceLanguage;
let source_manager = &config.context.session().source_manager;
let source_file = source_manager.load(SourceLanguage::Other("hir"), uri, source.into());
parse_anchored_source(None, config, source_file)
}
pub fn parse_anchored(
name: OperationName,
config: ParserConfig,
uri: Uri,
source: impl Into<String>,
) -> Result<OperationRef, Report> {
use midenc_session::diagnostics::SourceLanguage;
let source_manager = &config.context.session().source_manager;
let source_file = source_manager.load(SourceLanguage::Other("hir"), uri, source.into());
parse_anchored_source(Some(name), config, source_file)
}
#[cfg(feature = "std")]
pub fn parse_file_any(
config: ParserConfig,
path: impl AsRef<std::path::Path>,
) -> Result<OperationRef, Report> {
let source_manager = &config.context.session().source_manager;
let source_file = source_manager.load_file(path.as_ref()).map_err(Report::msg)?;
parse_anchored_source(None, config, source_file)
}
#[cfg(feature = "std")]
pub fn parse_file_anchored(
anchor: OperationName,
config: ParserConfig,
path: impl AsRef<std::path::Path>,
) -> Result<OperationRef, Report> {
let source_manager = &config.context.session().source_manager;
let source_file = source_manager.load_file(path.as_ref()).map_err(Report::msg)?;
parse_anchored_source(Some(anchor), config, source_file)
}
fn parse_anchored_source(
anchor_name: Option<OperationName>,
config: ParserConfig,
source_file: Arc<SourceFile>,
) -> Result<OperationRef, Report> {
use crate::{BuilderExt, dialects::builtin::World};
let source = source_file.as_str();
let scanner = Scanner::new(source);
let token_stream = TokenStream::new(source_file.id(), scanner);
let should_verify = config.should_verify_after_parse();
let mut parser = DefaultParser::new(ParserState::new(config, token_stream));
let span = parser.current_location();
let world = parser.builder_mut().create::<World, ()>(span)()?;
let anchor = world.as_operation_ref();
let mut asm_state = Box::<AsmParserState>::default();
{
asm_state.initialize(anchor);
parser.state_mut().asm_state = Some(asm_state);
}
let mut operation_parser = operation::OperationParser::new(parser, world);
let op = operation_parser
.parse_operation()
.map_err(|err| Report::from(err).with_source_code(source_file.clone()))?;
operation_parser
.finalize_without_verifying()
.map_err(|err| Report::from(err).with_source_code(source_file.clone()))?;
let root = if op.borrow().is::<World>() {
let mut parsed_world = op;
parsed_world.borrow_mut().remove();
op
} else {
anchor
};
if should_verify {
root.borrow().recursively_verify().map_err(|err| {
Report::from(ParserError::from(err)).with_source_code(source_file.clone())
})?;
}
match anchor_name {
None => Ok(op),
Some(anchor) => {
let operation = op.borrow();
if operation.name() != anchor {
return Err(Report::msg(format!(
"expected operation '{anchor}', got '{}'",
operation.name()
)));
}
Ok(op)
}
}
}
fn parse_source<T: OpParser + OpRegistration>(
config: ParserConfig,
source_file: Arc<SourceFile>,
) -> Result<UnsafeIntrusiveEntityRef<T>, Report> {
let name = config.context.get_registered_name::<T>();
let op = parse_anchored_source(Some(name), config, source_file)?;
match op.try_downcast_op::<T>() {
Ok(op) => Ok(op),
Err(_) => unreachable!("anchored parser returned the wrong operation type"),
}
}
pub trait OpAsmParser<'input>: Parser<'input> {
fn parse_optional_location_specifier(&mut self) -> ParseResult<Option<Location>>;
fn get_result_name(&self, result_num: u8) -> Option<(interner::Symbol, u8)>;
fn get_num_results(&self) -> usize;
fn parse_generic_operation(&mut self, ip: Option<ProgramPoint>) -> ParseResult<OperationRef>;
fn parse_custom_operation_name(&mut self) -> ParseResult<Span<OperationName>>;
fn parse_generic_operation_after_name(
&mut self,
state: &mut OperationState,
operands: Option<&[UnresolvedOperand]>,
successors: Option<&[BlockRef]>,
regions: Option<&[RegionRef]>,
attrs: Option<ParsedAttrs>,
signature: Option<FunctionType>,
) -> ParseResult;
fn parse_operand(&mut self, allow_result_number: bool) -> ParseResult<UnresolvedOperand>;
fn parse_optional_operand(
&mut self,
allow_result_number: bool,
) -> ParseResult<Option<UnresolvedOperand>>;
fn parse_operand_list(
&mut self,
result: &mut SmallVec<[UnresolvedOperand; 2]>,
delimiter: Delimiter,
allow_result_number: bool,
required_operand_count: Option<NonZeroU8>,
) -> ParseResult {
let start = self.current_location();
let base_len = result.len();
self.parse_comma_separated_list(delimiter, Some("operand list"), |parser| {
let operand = parser.parse_operand(allow_result_number)?;
result.push(operand);
Ok(required_operand_count
.is_none_or(|required| result.len() - base_len < required.get() as usize))
})?;
if let Some(required) = required_operand_count
&& result.len() - base_len != required.get() as usize
{
return Err(ParserError::InvalidOperandCount {
span: start,
expected: required.get() as usize,
actual: result.len() - base_len,
});
}
Ok(())
}
fn parse_trailing_operand_list(
&mut self,
result: &mut SmallVec<[UnresolvedOperand; 2]>,
delimiter: Delimiter,
) -> ParseResult {
if self.parse_optional_comma()? {
self.parse_operand_list(result, delimiter, true, None)
} else {
Ok(())
}
}
fn resolve_operand(&mut self, operand: UnresolvedOperand, ty: Type) -> ParseResult<ValueRef>;
fn resolve_operands_of_uniform_type(
&mut self,
operands: &[UnresolvedOperand],
ty: &Type,
result: &mut SmallVec<[ValueRef; 2]>,
) -> ParseResult {
for operand in operands.iter().copied() {
result.push(self.resolve_operand(operand, ty.clone())?);
}
Ok(())
}
fn resolve_operands(
&mut self,
span: SourceSpan,
operands: &[UnresolvedOperand],
tys: &[Type],
result: &mut SmallVec<[ValueRef; 2]>,
) -> ParseResult {
if operands.len() != tys.len() {
return Err(ParserError::OperandAndTypeListMismatch {
span,
num_operands: operands.len(),
num_types: tys.len(),
});
}
for (operand, ty) in operands.iter().copied().zip(tys.iter().cloned()) {
result.push(self.resolve_operand(operand, ty)?);
}
Ok(())
}
fn parse_argument(&mut self, allow_type: bool, allow_attrs: bool) -> ParseResult<Argument>;
fn parse_optional_argument(
&mut self,
allow_type: bool,
allow_attrs: bool,
) -> ParseResult<Option<Argument>>;
fn parse_argument_list(
&mut self,
delimiter: Delimiter,
allow_type: bool,
allow_attrs: bool,
result: &mut SmallVec<[Argument; 4]>,
) -> ParseResult {
self.parse_comma_separated_list(delimiter, Some("argument list"), |parser| {
let arg = parser.parse_optional_argument(allow_type, allow_attrs)?;
if let Some(arg) = arg {
result.push(arg);
Ok(true)
} else {
Ok(false)
}
})
}
fn parse_region(
&mut self,
region: RegionRef,
arguments: &[Argument],
enable_name_shadowing: bool,
) -> ParseResult;
fn parse_optional_region(
&mut self,
arguments: &[Argument],
enable_name_shadowing: bool,
) -> ParseResult<Option<RegionRef>>;
fn parse_optional_region_with_token(
&mut self,
keyword: &str,
arguments: &[Argument],
enable_name_shadowing: bool,
) -> ParseResult<Option<RegionRef>> {
if self.token_stream_mut().next_if_eq(Token::BareIdent(keyword))? {
let region = self.context().create_region();
self.parse_region(region, arguments, enable_name_shadowing)?;
Ok(Some(region))
} else {
Ok(None)
}
}
fn parse_successor(&mut self) -> ParseResult<Span<BlockRef>>;
fn parse_optional_successor(&mut self) -> ParseResult<Option<Span<BlockRef>>>;
fn parse_successor_and_use_list(
&mut self,
operands: &mut SmallVec<[ValueRef; 2]>,
) -> ParseResult<Span<BlockRef>>;
}
pub trait OpAsmDialectInterface: crate::Dialect {}
pub trait OpAsmOpInterface: crate::Op {
fn get_asm_result_names(&self) -> SmallVec<[(OpResultRef, Option<CompactString>); 2]> {
SmallVec::new_const()
}
fn get_asm_block_argument_names(
&self,
region: RegionRef,
) -> SmallOrdMap<BlockArgumentRef, CompactString, 4> {
SmallOrdMap::new()
}
fn get_asm_block_names(&self) -> SmallOrdMap<BlockRef, CompactString, 1> {
SmallOrdMap::new()
}
fn get_default_dialect(&self) -> Option<interner::Symbol> {
None
}
}
#[derive(Debug)]
pub struct Argument {
pub name: UnresolvedOperand,
pub ty: Type,
pub attrs: ParsedAttrs,
pub loc: Location,
}
impl Argument {
pub fn has_attribute(&self, name: impl Into<interner::Symbol>) -> bool {
let name = name.into();
self.attrs.iter().any(|attr| attr.name == name)
}
}
#[derive(Copy, Clone)]
pub struct UnresolvedOperand {
pub loc: SourceSpan,
pub name: ValueId,
}
impl core::fmt::Debug for UnresolvedOperand {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("UnresolvedOperand")
.field_with("name", |f| write!(f, "{}", self.name))
.finish()
}
}
pub type ParsedAttrs = SmallVec<[NamedAttribute; 1]>;