use std::any::{Any, TypeId};
use telltale_runtime::{
ast::LocalType, compiler::projection::ProjectionError, CodegenContext, ExtensionParserBuilder,
ExtensionRegistry, ExtensionValidationError, GrammarExtension, ParseContext, ParseError,
ProjectionContext, ProtocolExtension, Role, StatementParser,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Telltale DSL Extension System Demo");
demo_timeout_extension()?;
demo_annotation_extension()?;
demo_extension_composition()?;
println!("All extension demos completed successfully!");
Ok(())
}
fn demo_timeout_extension() -> Result<(), Box<dyn std::error::Error>> {
println!("\nDemo 1: Timeout Extension");
let mut registry = ExtensionRegistry::new();
telltale_runtime::extensions::timeout::register_timeout_extension(&mut registry)
.expect("timeout extension should register");
println!("Timeout extension registered");
println!(
"Can handle 'timeout_stmt': {}",
registry.can_handle("timeout_stmt")
);
Ok(())
}
fn demo_annotation_extension() -> Result<(), Box<dyn std::error::Error>> {
println!("\nDemo 2: Custom Annotation Extension");
let parser = ExtensionParserBuilder::new()
.with_extension(PriorityGrammarExtension, PriorityStatementParser)
.expect("priority extension should register")
.build();
let _choreography_with_priority = r#"
protocol PriorityExample = {
roles Client, Server
Client -> Server : Request
priority high Server -> Client : UrgentResponse
}
"#;
println!("Priority extension registered");
println!(
"Can handle 'priority_stmt': {}",
parser.can_handle_statement("priority_stmt")
);
Ok(())
}
fn demo_extension_composition() -> Result<(), Box<dyn std::error::Error>> {
println!("\nDemo 3: Extension Composition");
let parser = ExtensionParserBuilder::new()
.with_extension(PriorityGrammarExtension, PriorityStatementParser)
.expect("priority extension should register")
.with_extension(LoggingGrammarExtension, LoggingStatementParser)
.expect("logging extension should register")
.build();
let stats = parser.extension_stats();
println!("Registered {} grammar extensions", stats.grammar_extensions);
match parser.get_composed_grammar() {
Ok(grammar) => {
println!(
"Successfully composed grammar with {} lines",
grammar.lines().count()
);
println!("Grammar sample (first 10 lines):");
for (i, line) in grammar.lines().take(10).enumerate() {
println!(" {}: {}", i + 1, line);
}
if grammar.lines().count() > 10 {
println!(" ... ({} more lines)", grammar.lines().count() - 10);
}
}
Err(e) => {
println!("Grammar composition failed: {}", e);
}
}
Ok(())
}
#[derive(Debug)]
struct PriorityGrammarExtension;
impl GrammarExtension for PriorityGrammarExtension {
fn grammar_rules(&self) -> &'static str {
r#"
priority_stmt = { "priority" ~ priority_level ~ send_stmt }
priority_level = { "high" | "medium" | "low" | "urgent" }
"#
}
fn statement_rules(&self) -> Vec<&'static str> {
vec!["priority_stmt"]
}
fn priority(&self) -> u32 {
150 }
fn extension_id(&self) -> &'static str {
"priority"
}
}
#[derive(Debug)]
struct PriorityStatementParser;
impl StatementParser for PriorityStatementParser {
fn can_parse(&self, rule_name: &str) -> bool {
rule_name == "priority_stmt"
}
fn supported_rules(&self) -> Vec<String> {
vec!["priority_stmt".to_string()]
}
fn parse_statement(
&self,
_rule_name: &str,
content: &str,
_context: &ParseContext,
) -> Result<Box<dyn ProtocolExtension>, ParseError> {
let priority = self.extract_priority(content)?;
Ok(Box::new(PriorityProtocol {
level: priority,
}))
}
}
impl PriorityStatementParser {
fn extract_priority(&self, content: &str) -> Result<PriorityLevel, ParseError> {
if content.contains("urgent") {
Ok(PriorityLevel::Urgent)
} else if content.contains("high") {
Ok(PriorityLevel::High)
} else if content.contains("medium") {
Ok(PriorityLevel::Medium)
} else if content.contains("low") {
Ok(PriorityLevel::Low)
} else {
Err(ParseError::InvalidSyntax {
details: "Unknown priority level".to_string(),
})
}
}
}
#[derive(Debug, Clone, Copy)]
enum PriorityLevel {
Urgent,
High,
Medium,
Low,
}
#[derive(Debug, Clone)]
struct PriorityProtocol {
level: PriorityLevel,
}
impl ProtocolExtension for PriorityProtocol {
fn type_name(&self) -> &'static str {
"PriorityProtocol"
}
fn mentions_role(&self, _role: &Role) -> bool {
false
}
fn validate(&self, _roles: &[Role]) -> Result<(), ExtensionValidationError> {
Ok(())
}
fn project(
&self,
_role: &Role,
_context: &ProjectionContext,
) -> Result<LocalType, ProjectionError> {
Ok(LocalType::End)
}
fn generate_code(&self, _context: &CodegenContext) -> proc_macro2::TokenStream {
let priority_str = format!("{:?}", self.level);
quote::quote! {
.with_priority(#priority_str)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
fn clone_box(&self) -> Box<dyn ProtocolExtension> {
Box::new(self.clone())
}
}
#[derive(Debug)]
struct LoggingGrammarExtension;
impl GrammarExtension for LoggingGrammarExtension {
fn grammar_rules(&self) -> &'static str {
r#"
logging_stmt = { "log" ~ logging_level ~ logging_string_literal }
logging_level = { "debug" | "info" | "warn" | "error" }
logging_string_literal = { "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
"#
}
fn statement_rules(&self) -> Vec<&'static str> {
vec!["logging_stmt"]
}
fn extension_id(&self) -> &'static str {
"logging"
}
}
#[derive(Debug)]
struct LoggingStatementParser;
impl StatementParser for LoggingStatementParser {
fn can_parse(&self, rule_name: &str) -> bool {
rule_name == "logging_stmt"
}
fn supported_rules(&self) -> Vec<String> {
vec!["logging_stmt".to_string()]
}
fn parse_statement(
&self,
_rule_name: &str,
_content: &str,
_context: &ParseContext,
) -> Result<Box<dyn ProtocolExtension>, ParseError> {
Ok(Box::new(LoggingProtocol {
message: "Example log message".to_string(),
}))
}
}
#[derive(Debug, Clone)]
struct LoggingProtocol {
message: String,
}
impl ProtocolExtension for LoggingProtocol {
fn type_name(&self) -> &'static str {
"LoggingProtocol"
}
fn mentions_role(&self, _role: &Role) -> bool {
false
}
fn validate(&self, _roles: &[Role]) -> Result<(), ExtensionValidationError> {
Ok(())
}
fn project(
&self,
_role: &Role,
_context: &ProjectionContext,
) -> Result<LocalType, ProjectionError> {
Ok(LocalType::End)
}
fn generate_code(&self, _context: &CodegenContext) -> proc_macro2::TokenStream {
let msg = &self.message;
quote::quote! {
.with_logging(#msg)
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
fn clone_box(&self) -> Box<dyn ProtocolExtension> {
Box::new(self.clone())
}
}
#[allow(unused_macros)]
macro_rules! simple_extension {
($name:ident, $rule:literal, $code:expr) => {
#[derive(Debug)]
struct $name;
impl GrammarExtension for $name {
fn grammar_rules(&self) -> &'static str {
$rule
}
fn statement_rules(&self) -> Vec<&'static str> {
vec![stringify!($name)]
}
fn extension_id(&self) -> &'static str {
stringify!($name)
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_priority_extension() {
let ext = PriorityGrammarExtension;
assert_eq!(ext.extension_id(), "priority");
assert!(ext.statement_rules().contains(&"priority_stmt"));
}
#[test]
fn test_logging_extension() {
let ext = LoggingGrammarExtension;
assert_eq!(ext.extension_id(), "logging");
assert!(ext.statement_rules().contains(&"logging_stmt"));
}
#[test]
fn test_extension_parser_builder() {
let parser = ExtensionParserBuilder::new()
.with_extension(PriorityGrammarExtension, PriorityStatementParser)
.expect("priority extension should register")
.with_extension(LoggingGrammarExtension, LoggingStatementParser)
.expect("logging extension should register")
.build();
assert!(parser.can_handle_statement("priority_stmt"));
assert!(parser.can_handle_statement("logging_stmt"));
assert!(!parser.can_handle_statement("unknown_stmt"));
}
}