use weavy::ir::{
EffectContract, EffectResource, IntrinsicDescriptor, IntrinsicOp, WeavyLowered, WeavyOp,
};
pub type SnarkWeavyLowered = WeavyLowered<SnarkBlockId, SnarkIntrinsic>;
pub type SnarkWeavyOp = WeavyOp<SnarkBlockId, SnarkIntrinsic>;
macro_rules! id_type {
($name:ident, $doc:literal) => {
#[doc = $doc]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name(u32);
impl $name {
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
};
}
id_type!(
SnarkBlockId,
"Symbolic block identity in a Snark Weavy lowering."
);
id_type!(
ParseStateId,
"Snark parser state identity generated from validated grammar facts."
);
id_type!(
SymbolId,
"Snark symbol identity generated from validated grammar facts."
);
id_type!(
TerminalId,
"Snark terminal symbol identity generated from validated grammar facts."
);
id_type!(
NonterminalId,
"Snark nonterminal symbol identity generated from validated grammar facts."
);
id_type!(
ProductionId,
"Snark production identity generated from validated grammar facts."
);
id_type!(
FieldId,
"Snark field identity generated from validated grammar facts."
);
id_type!(
LexModeId,
"Snark lexical mode identity generated from validated grammar facts."
);
id_type!(
AliasSequenceId,
"Snark alias-sequence identity generated from validated grammar facts."
);
id_type!(
ExternalScannerStateId,
"Snark external scanner state identity for scanner replay."
);
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum SnarkIntrinsic {
Lex {
mode: LexModeId,
},
CallExternalScanner {
state: ParseStateId,
scanner_state: ExternalScannerStateId,
},
Shift {
state: ParseStateId,
},
Reduce {
production: ProductionId,
symbol: NonterminalId,
aliases: Option<AliasSequenceId>,
},
Recover {
state: ParseStateId,
},
EmitNode {
symbol: SymbolId,
},
EmitCapture {
field: Option<FieldId>,
},
}
impl SnarkIntrinsic {
pub const DIALECT: &'static str = "snark.tree_sitter";
}
impl IntrinsicOp for SnarkIntrinsic {
fn descriptor(&self) -> IntrinsicDescriptor {
let name = match self {
Self::Lex { .. } => "lex",
Self::CallExternalScanner { .. } => "call_external_scanner",
Self::Shift { .. } => "shift",
Self::Reduce { .. } => "reduce",
Self::Recover { .. } => "recover",
Self::EmitNode { .. } => "emit_node",
Self::EmitCapture { .. } => "emit_capture",
};
IntrinsicDescriptor {
dialect: Self::DIALECT,
name,
}
}
fn effect(&self) -> EffectContract {
match self {
Self::Lex { .. } => EffectContract::new()
.read_resource(EffectResource::Input("source"))
.advance_resource(EffectResource::Input("source"))
.may_fail(),
Self::CallExternalScanner { .. } => EffectContract::new()
.read_resource(EffectResource::Input("source"))
.advance_resource(EffectResource::Input("source"))
.read_resource(EffectResource::SideChannel("scanner_state"))
.write_resource(EffectResource::SideChannel("scanner_state"))
.may_fail()
.calls_user_code(),
Self::Shift { .. } => EffectContract::new()
.read_resource(EffectResource::SideChannel("parser_stack"))
.write_resource(EffectResource::SideChannel("parser_stack")),
Self::Reduce { .. } => EffectContract::new()
.read_resource(EffectResource::SideChannel("parser_stack"))
.write_resource(EffectResource::SideChannel("parser_stack"))
.write_resource(EffectResource::Sink("tree_events")),
Self::Recover { .. } => EffectContract::new()
.read_resource(EffectResource::Input("source"))
.advance_resource(EffectResource::Input("source"))
.write_resource(EffectResource::SideChannel("parser_stack"))
.write_resource(EffectResource::Sink("tree_events"))
.may_fail(),
Self::EmitNode { .. } => {
EffectContract::new().write_resource(EffectResource::Sink("tree_events"))
}
Self::EmitCapture { .. } => {
EffectContract::new().write_resource(EffectResource::Sink("query_captures"))
}
}
}
}
#[must_use]
pub fn empty_lowered() -> SnarkWeavyLowered {
WeavyLowered::new(Vec::new())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intrinsic_descriptors_use_snark_tree_sitter_dialect() {
let descriptor = SnarkIntrinsic::Shift {
state: ParseStateId(7),
}
.descriptor();
assert_eq!(descriptor.dialect, "snark.tree_sitter");
assert_eq!(descriptor.name, "shift");
}
#[test]
fn empty_lowered_has_no_program_or_blocks() {
let lowered = empty_lowered();
assert!(lowered.program.is_empty());
assert!(lowered.blocks.is_empty());
}
}