diff --git a/src/protocol/ast_printer.rs b/src/protocol/ast_printer.rs new file mode 100644 index 0000000000000000000000000000000000000000..3b3bed69397c541960531b11c2ebae411f07155b --- /dev/null +++ b/src/protocol/ast_printer.rs @@ -0,0 +1,353 @@ +use std::fmt::{Display, Write}; +use std::io::Write as IOWrite; + +use super::ast::*; +use std::borrow::Borrow; + +const INDENT: usize = 2; + +const PREFIX_EMPTY: &'static str = " "; +const PREFIX_ROOT_ID: &'static str = "Root"; +const PREFIX_PRAGMA_ID: &'static str = "Prag"; +const PREFIX_IMPORT_ID: &'static str = "Imp "; +const PREFIX_TYPE_ANNOT_ID: &'static str = "TyAn"; +const PREFIX_VARIABLE_ID: &'static str = "Var "; +const PREFIX_PARAMETER_ID: &'static str = "Par "; +const PREFIX_LOCAL_ID: &'static str = "Loc "; +const PREFIX_DEFINITION_ID: &'static str = "Def "; +const PREFIX_STRUCT_ID: &'static str = "DefS"; +const PREFIX_ENUM_ID: &'static str = "DefE"; +const PREFIX_COMPONENT_ID: &'static str = "DefC"; +const PREFIX_FUNCTION_ID: &'static str = "DefF"; +const PREFIX_STMT_ID: &'static str = "Stmt"; +const PREFIX_BLOCK_STMT_ID: &'static str = "SBl "; +const PREFIX_LOCAL_STMT_ID: &'static str = "SLoc"; +const PREFIX_MEM_STMT_ID: &'static str = "SMem"; +const PREFIX_CHANNEL_STMT_ID: &'static str = "SCha"; +const PREFIX_SKIP_STMT_ID: &'static str = "SSki"; +const PREFIX_LABELED_STMT_ID: &'static str = "SLab"; +const PREFIX_IF_STMT_ID: &'static str = "SIf "; +const PREFIX_ENDIF_STMT_ID: &'static str = "SEIf"; +const PREFIX_WHILE_STMT_ID: &'static str = "SWhi"; +const PREFIX_ENDWHILE_STMT_ID: &'static str = "SEWh"; +const PREFIX_BREAK_STMT_ID: &'static str = "SBre"; +const PREFIX_CONTINUE_STMT_ID: &'static str = "SCon"; +const PREFIX_SYNC_STMT_ID: &'static str = "SSyn"; +const PREFIX_ENDSYNC_STMT_ID: &'static str = "SESy"; +const PREFIX_RETURN_STMT_ID: &'static str = "SRet"; +const PREFIX_ASSERT_STMT_ID: &'static str = "SAsr"; +const PREFIX_GOTO_STMT_ID: &'static str = "SGot"; +const PREFIX_NEW_STMT_ID: &'static str = "SNew"; +const PREFIX_PUT_STMT_ID: &'static str = "SPut"; +const PREFIX_EXPR_STMT_ID: &'static str = "SExp"; +const PREFIX_ASSIGNMENT_EXPR_ID: &'static str = "EAsi"; +const PREFIX_CONDITIONAL_EXPR_ID: &'static str = "ECnd"; +const PREFIX_BINARY_EXPR_ID: &'static str = "EBin"; +const PREFIX_UNARY_EXPR_ID: &'static str = "EUna"; +const PREFIX_INDEXING_EXPR_ID: &'static str = "EIdx"; +const PREFIX_SLICING_EXPR_ID: &'static str = "ESli"; +const PREFIX_SELECT_EXPR_ID: &'static str = "ESel"; +const PREFIX_ARRAY_EXPR_ID: &'static str = "EArr"; +const PREFIX_CONST_EXPR_ID: &'static str = "ECns"; +const PREFIX_CALL_EXPR_ID: &'static str = "ECll"; +const PREFIX_VARIABLE_EXPR_ID: &'static str = "EVar"; + +pub(crate) struct ASTWriter { + buffer: String, + temp: String, +} + +impl ASTWriter { + pub(crate) fn new() -> Self { + Self{ buffer: String::with_capacity(4096), temp: String::with_capacity(256) } + } + pub(crate) fn write_ast(&mut self, w: &mut W, heap: &Heap) { + for root_id in heap.protocol_descriptions.iter().map(|v| v.this) { + self.write_module(heap, root_id); + w.write_all(self.buffer.as_bytes()).expect("flush buffer"); + self.buffer.clear(); + } + } + + //-------------------------------------------------------------------------- + // Top-level module writing + //-------------------------------------------------------------------------- + + fn write_module(&mut self, heap: &Heap, root_id: RootId) { + self.write_id_and_indent(PREFIX_ROOT_ID, root_id.0.index, 0); + self.buffer.write_str("Module:\n"); + + let root = &heap[root_id]; + + self.write_indent(0); + write!(&mut self.buffer, "- ID: {}\n", root.this.0.index); + + self.write_indent(0); + self.buffer.write_str("- Pragmas:\n"); + + for pragma_id in &root.pragmas { + self.write_pragma(&heap[*pragma_id], 1); + } + + self.write_indent(0); + self.buffer.write_str("- Imports:\n"); + + for import_id in &root.imports { + self.write_import(&heap[*import_id], 1); + } + + self.write_indent(0); + self.buffer.write_str("- Definitions:\n"); + + for definition_id in &root.definitions { + self.write_definition(heap, &heap[*definition_id], 1); + } + } + + fn write_pragma(&mut self, pragma: &Pragma, indent: usize) { + match pragma { + Pragma::Version(pragma) => { + self.write_id_and_indent(PREFIX_PRAGMA_ID, pragma.this.0.index, indent); + write!(&mut self.buffer, "- Version: {}\n", pragma.version); + }, + Pragma::Module(pragma) => { + self.write_id_and_indent(PREFIX_PRAGMA_ID, pragma.this.0.index, indent); + write!(&mut self.buffer, "- Module: {}\n", String::from_utf8_lossy(&pragma.value)); + } + } + } + + fn write_import(&mut self, import: &Import, indent: usize) { + let indent2 = indent + 1; + match import { + Import::Module(import) => { + self.write_id_and_indent(PREFIX_IMPORT_ID, import.this.0.index, indent); + write!(&mut self.buffer, "- ModuleImport:\n"); + + self.write_indent(indent2); + write!(&mut self.buffer, "- Name: {}\n", String::from_utf8_lossy(&import.module_name)); + self.write_indent(indent2); + write!(&mut self.buffer, "- Alias: {}\n", String::from_utf8_lossy(&import.alias)); + self.write_indent(indent2); + write_option(&mut self.temp, import.module_id.map(|v| v.0.index)); + write!(&mut self.buffer, "- Target: {}\n", &self.temp); + }, + Import::Symbols(import) => { + self.write_id_and_indent(PREFIX_IMPORT_ID, import.this.0.index, indent); + write!(&mut self.buffer, "- SymbolImport:\n"); + + self.write_indent(indent2); + write!(&mut self.buffer, "- Name: {}\n", String::from_utf8_lossy(&import.module_name)); + self.write_indent(indent2); + write_option(&mut self.temp, import.module_id.map(|v| v.0.index)); + write!(&mut self.buffer, "- Target: {}\n", &self.temp); + + self.write_indent(indent2); + write!(&mut self.buffer, "- Symbols:\n"); + + let indent3 = indent2 + 1; + let indent4 = indent3 + 1; + for symbol in &import.symbols { + self.write_indent(indent3); + write!(&mut self.buffer, "- AliasedSymbol:\n"); + self.write_indent(indent4); + write!(&mut self.buffer, "- Name: {}\n", String::from_utf8_lossy(&symbol.name)); + self.write_indent(indent4); + write!(&mut self.buffer, "- Alias: {}\n", String::from_utf8_lossy(&symbol.alias)); + self.write_indent(indent4); + write_option(&mut self.temp, symbol.definition_id.map(|v| v.0.index)); + write!(&mut self.buffer, "- Definition: {}\n", &self.temp); + } + } + } + } + + //-------------------------------------------------------------------------- + // Top-level definition writing + //-------------------------------------------------------------------------- + + fn write_definition(&mut self, heap: &Heap, def: &Definition, indent: usize) { + match def { + Definition::Struct(_) => todo!("implement Definition::Struct"), + Definition::Enum(_) => todo!("implement Definition::Enum"), + Definition::Function(_) => todo!("implement Definition::Function"), + Definition::Component(def) => { + self.write_id_and_indent(PREFIX_COMPONENT_ID, def.this.0.0.index, indent); + write!(&mut self.buffer, "- Component:\n"); + + let indent2 = indent + 1; + let indent3 = indent2 + 1; + let indent4 = indent3 + 1; + self.write_indent(indent2); + write!(&mut self.buffer, "- Name: {}\n", String::from_utf8_lossy(&def.identifier.value)); + self.write_indent(indent2); + write!(&mut self.buffer, "- Variant: {:?}\n", &def.variant); + self.write_indent(indent2); + write!(&mut self.buffer, "- Parameters:\n"); + + for parameter_id in &def.parameters { + let param = &heap[*parameter_id]; + self.write_indent(indent3); + write!(&mut self.buffer, "- Parameter\n"); + + self.write_indent(indent4); + write!(&mut self.buffer, "- Name: {}\n", String::from_utf8_lossy(¶m.identifier.value)); + self.write_indent(indent4); + write!(&mut self.buffer, "- Type: "); + write_type(&mut self.buffer, &heap[param.type_annotation]); + self.buffer.push('\n'); + } + + self.write_indent(indent2); + write!(&mut self.buffer, "- Body:\n"); + + self.write_stmt(heap, def.body, indent3); + } + } + } + + fn write_stmt(&mut self, heap: &Heap, stmt_id: StatementId, indent: usize) { + let stmt = &heap[stmt_id]; + + match stmt { + Statement::Block(stmt) => { + self.write_id_and_indent(PREFIX_BLOCK_STMT_ID, stmt.this.0.0.index, indent); + write!(&mut self.buffer, "- Block:\n"); + for stmt_id in &stmt.statements { + self.write_stmt(heap, *stmt_id, indent + 1); + } + }, + Statement::Local(stmt) => { + match stmt { + LocalStatement::Channel(stmt) => { + self.write_id_and_indent(PREFIX_CHANNEL_STMT_ID, stmt.this.0.0.0.index, indent); + write!(&mut self.buffer, "- Channel:\n"); + + let indent2 = indent + 1; + let indent3 = indent2 + 1; + let from = &heap[stmt.from]; + self.write_id_and_indent(PREFIX_LOCAL_ID, stmt.from.0.0.index, indent2); + }, + LocalStatement::Memory(stmt) => { + + } + } + }, + Statement::Skip(stmt) => { + self.write_id_and_indent(PREFIX_SKIP_STMT_ID, stmt.this.0.0.index, indent); + write!(&mut self.buffer, "- Skip\n"); + }, + Statement::Labeled(stmt) => { + self.write_id_and_indent(PREFIX_LABELED_STMT_ID, stmt.this.0.0.index, indent); + write!(&mut self.buffer, "- LabeledStatement\n"); + + let indent1 = indent + 1; + let indent2 = indent + 2; + self.write_indent(indent1); + write!(&mut self.buffer, "- Label: {}\n", String::from_utf8_lossy(&stmt.label.value)); + self.write_indent(indent1); + write!(&mut self.buffer, "- Statement:\n"); + self.write_stmt(heap, stmt.body, indent2); + }, + Statement::If(stmt) => { + + }, + Statement::EndIf(stmt) => { + + }, + Statement::While(stmt) => { + + }, + Statement::EndWhile(stmt) => { + + }, + Statement::Break(stmt) => { + + }, + Statement::Continue(stmt) => { + + }, + Statement::Synchronous(stmt) => { + + }, + Statement::EndSynchronous(stmt) => { + + }, + Statement::Return(stmt) => { + + }, + Statement::Assert(stmt) => { + + }, + Statement::Goto(stmt) => { + + }, + Statement::New(stmt) => { + + }, + Statement::Put(stmt) => { + + }, + Statement::Expression(stmt) => { + + } + } + } + + fn write_local(&mut self, heap: &Heap, local_id: LocalId, indent: usize) { + o + } + + //-------------------------------------------------------------------------- + // Printing Utilities + //-------------------------------------------------------------------------- + + fn write_id_and_indent(&mut self, prefix: &'static str, id: u32, indent: usize) { + write!(&mut self.buffer, "{}[{:04}] ", prefix, id); + for _ in 0..indent*INDENT { + self.buffer.push(' '); + } + } + + fn write_indent(&mut self, indent: usize) { + write!(&mut self.buffer, "{} ", PREFIX_EMPTY); + for _ in 0..indent*INDENT { + self.buffer.push(' '); + } + } + + fn flush(&mut self, w: &mut W) { + w.write(self.buffer.as_bytes()).unwrap(); + self.buffer.clear() + } +} + +fn write_option(target: &mut String, value: Option) { + target.clear(); + match &value { + Some(v) => write!(target, "Some({})", v), + None => target.write_str("None") + }; +} + +fn write_type(target: &mut String, t: &TypeAnnotation) { + match &t.the_type.primitive { + PrimitiveType::Input => target.write_str("in"), + PrimitiveType::Output => target.write_str("out"), + PrimitiveType::Message => target.write_str("msg"), + PrimitiveType::Boolean => target.write_str("bool"), + PrimitiveType::Byte => target.write_str("byte"), + PrimitiveType::Short => target.write_str("short"), + PrimitiveType::Int => target.write_str("int"), + PrimitiveType::Long => target.write_str("long"), + PrimitiveType::Symbolic(symbolic) => { + let mut temp = String::new(); + write_option(&mut temp, symbolic.definition.map(|v| v.0.index)); + write!(target, "Symbolic(name: {}, target: {})", String::from_utf8_lossy(&symbolic.identifier.value), &temp); + } + }; + + if t.the_type.array { + target.push_str("[]"); + } +} \ No newline at end of file