Compare commits
	
		
			13 Commits
		
	
	
		
			6ab9efa8fd
			...
			3026d22750
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3026d22750 | ||
|   | e0f2810764 | ||
|   | 692411e232 | ||
|   | bf06407d16 | ||
|   | 35d616a538 | ||
|   | 2de45817d4 | ||
|   | 78e7271950 | ||
|   | bae2048aef | ||
|   | 17285e84eb | ||
|   | 15abcc92d3 | ||
|   | f5a82c414c | ||
|   | dda86f75e7 | ||
|   | 4224055860 | 
							
								
								
									
										38
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										38
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -148,6 +148,8 @@ version = "0.1.0" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "clap", |  "clap", | ||||||
|  "codespan-reporting", |  "codespan-reporting", | ||||||
|  |  "indoc", | ||||||
|  |  "log", | ||||||
|  "pest", |  "pest", | ||||||
|  "pest_derive", |  "pest_derive", | ||||||
| ] | ] | ||||||
| @ -178,6 +180,12 @@ version = "0.5.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "indoc" | ||||||
|  | version = "2.0.6" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "is_terminal_polyfill" | name = "is_terminal_polyfill" | ||||||
| version = "1.70.1" | version = "1.70.1" | ||||||
| @ -190,6 +198,12 @@ version = "0.2.167" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" | checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "log" | ||||||
|  | version = "0.4.27" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "memchr" | name = "memchr" | ||||||
| version = "2.7.4" | version = "2.7.4" | ||||||
| @ -204,9 +218,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "pest" | name = "pest" | ||||||
| version = "2.7.14" | version = "2.8.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" | checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "memchr", |  "memchr", | ||||||
|  "thiserror", |  "thiserror", | ||||||
| @ -215,9 +229,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "pest_derive" | name = "pest_derive" | ||||||
| version = "2.7.14" | version = "2.8.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" | checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "pest", |  "pest", | ||||||
|  "pest_generator", |  "pest_generator", | ||||||
| @ -225,9 +239,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "pest_generator" | name = "pest_generator" | ||||||
| version = "2.7.14" | version = "2.8.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" | checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "pest", |  "pest", | ||||||
|  "pest_meta", |  "pest_meta", | ||||||
| @ -238,9 +252,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "pest_meta" | name = "pest_meta" | ||||||
| version = "2.7.14" | version = "2.8.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" | checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "once_cell", |  "once_cell", | ||||||
|  "pest", |  "pest", | ||||||
| @ -324,18 +338,18 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "thiserror" | name = "thiserror" | ||||||
| version = "1.0.69" | version = "2.0.12" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "thiserror-impl", |  "thiserror-impl", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "thiserror-impl" | name = "thiserror-impl" | ||||||
| version = "1.0.69" | version = "2.0.12" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
|  | |||||||
| @ -12,7 +12,9 @@ name = "dmc" | |||||||
| path = "src/bin/dmc/main.rs" | path = "src/bin/dmc/main.rs" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| pest = "2.7.14" | pest = { version = "2.8.0" } | ||||||
| clap = { version = "4.5.23", features = ["derive"] } | clap = { version = "4.5.23", features = ["derive"] } | ||||||
| pest_derive = "2.7.14" | pest_derive = { version = "2.8.0", features = ["grammar-extras"] } | ||||||
| codespan-reporting = "0.12.0" | codespan-reporting = "0.12.0" | ||||||
|  | log = "0.4.27" | ||||||
|  | indoc = "2.0.6" | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								sketching/may_2025/closure.dm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								sketching/may_2025/closure.dm
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | fn main() { | ||||||
|  |     let x = 42; | ||||||
|  |     let cl = mut |&mut x| { i -> | ||||||
|  |         let result = x + i; | ||||||
|  |         x = x + 1; | ||||||
|  |         result | ||||||
|  |     }; | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								sketching/may_2025/if.dm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								sketching/may_2025/if.dm
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | fn main(args: Array<String>) { | ||||||
|  |     if (args[0] == 'test') { | ||||||
|  |         println('test'); | ||||||
|  |     } else if (args[0] == 'foo') { | ||||||
|  |         println('foo'); | ||||||
|  |     } else { | ||||||
|  |         println('not test'); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,13 +1,17 @@ | |||||||
| ns greeter | ns greeter | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main(x: String) { | ||||||
|     let x = 'Hello'; |     let x = 'Hello'; | ||||||
|     let y = 'World'; |     let y = 'World'; | ||||||
|     { |     { | ||||||
|         let x = 'Hello'; |         let test = 'test'; | ||||||
|         let test = 'Test'; |         let x = 'x'; | ||||||
|         let test = 'Again'; |         let y = 'y'; | ||||||
|  |         let z = 'z'; | ||||||
|  |         let test = 'oops.'; | ||||||
|     }; |     }; | ||||||
|     x = y; | } | ||||||
|     let x = 'Hello!'; | 
 | ||||||
|  | pub mod test { | ||||||
|  | 
 | ||||||
| } | } | ||||||
							
								
								
									
										1024
									
								
								src/ast/build.rs
									
									
									
									
									
								
							
							
						
						
									
										1024
									
								
								src/ast/build.rs
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										112
									
								
								src/ast/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/ast/mod.rs
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | |||||||
| use crate::compile::name_analysis::Symbol; | use crate::name_analysis::symbol::Symbol; | ||||||
| use pest::Parser; | use pest::Parser; | ||||||
| use std::range::Range; | use std::range::Range; | ||||||
| 
 | 
 | ||||||
| @ -55,15 +55,17 @@ pub enum SuffixUnaryOperator { | |||||||
| #[derive(Debug, Clone)] | #[derive(Debug, Clone)] | ||||||
| pub struct Identifier { | pub struct Identifier { | ||||||
|     pub name: String, |     pub name: String, | ||||||
|  |     pub file_id: usize, | ||||||
|     pub range: Range<usize>, |     pub range: Range<usize>, | ||||||
|     scope_id: Option<usize>, |     scope_id: Option<usize>, | ||||||
|     symbol: Option<Symbol>, |     symbol: Option<Symbol>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Identifier { | impl Identifier { | ||||||
|     pub fn new(name: &str, range: Range<usize>) -> Self { |     pub fn new(name: &str, file_id: usize, range: Range<usize>) -> Self { | ||||||
|         Identifier { |         Identifier { | ||||||
|             name: name.to_string(), |             name: name.to_string(), | ||||||
|  |             file_id, | ||||||
|             range, |             range, | ||||||
|             scope_id: None, |             scope_id: None, | ||||||
|             symbol: None, |             symbol: None, | ||||||
| @ -90,16 +92,18 @@ impl Identifier { | |||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct FullyQualifiedName { | pub struct FullyQualifiedName { | ||||||
|     pub identifiers: Vec<Identifier>, |     pub identifiers: Vec<Identifier>, | ||||||
|  |     pub file_id: usize, | ||||||
|     pub range: Range<usize>, |     pub range: Range<usize>, | ||||||
|     scope_id: Option<usize>, |     scope_id: Option<usize>, | ||||||
|     symbol: Option<Symbol>, |     symbol: Option<Symbol>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FullyQualifiedName { | impl FullyQualifiedName { | ||||||
|     pub fn new(identifiers: Vec<Identifier>, range: Range<usize>) -> Self { |     pub fn new(identifiers: Vec<Identifier>, file_id: usize, range: Range<usize>) -> Self { | ||||||
|         FullyQualifiedName { |         FullyQualifiedName { | ||||||
|             identifiers, |             identifiers, | ||||||
|             range, |             range, | ||||||
|  |             file_id, | ||||||
|             scope_id: None, |             scope_id: None, | ||||||
|             symbol: None, |             symbol: None, | ||||||
|         } |         } | ||||||
| @ -241,8 +245,8 @@ impl Default for Parameters { | |||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Parameter { | pub struct Parameter { | ||||||
|     identifier: Identifier, |     pub identifier: Identifier, | ||||||
|     type_use: TypeUse, |     pub type_use: TypeUse, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Return Type */ | /* Return Type */ | ||||||
| @ -491,15 +495,17 @@ pub struct IfStatement { | |||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct IfElseStatement { | pub struct IfElseStatement { | ||||||
|     pub condition: Expression, |     pub if_statement: IfStatement, | ||||||
|     pub then_block: BlockStatement, |  | ||||||
|     pub else_ifs: ElseIfs, |     pub else_ifs: ElseIfs, | ||||||
|     pub else_block: BlockStatement, |     pub else_block: Option<ElseBlock>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug, Default)] | ||||||
| pub struct ElseIfs(pub Vec<IfStatement>); | pub struct ElseIfs(pub Vec<IfStatement>); | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct ElseBlock(pub BlockStatement); | ||||||
|  | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct WhileStatement { | pub struct WhileStatement { | ||||||
|     pub condition: Expression, |     pub condition: Expression, | ||||||
| @ -571,7 +577,51 @@ pub struct CallArguments(pub Vec<CallArgument>); | |||||||
| pub struct CallArgument(pub Box<Expression>); | pub struct CallArgument(pub Box<Expression>); | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Closure {} | pub struct Closure { | ||||||
|  |     pub modifier: Option<ClosureModifier>, | ||||||
|  |     pub is_move: bool, | ||||||
|  |     pub captures: ClosureCaptures, | ||||||
|  |     pub parameters: ClosureParameters, | ||||||
|  |     pub statements: Vec<Statement>, | ||||||
|  |     pub expression: Option<Box<Expression>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub enum ClosureModifier { | ||||||
|  |     Cons, | ||||||
|  |     Mut, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub struct ClosureCaptures(pub Vec<ClosureCapture>); | ||||||
|  | 
 | ||||||
|  | impl ClosureCaptures { | ||||||
|  |     pub fn is_empty(&self) -> bool { | ||||||
|  |         self.0.is_empty() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct ClosureCapture { | ||||||
|  |     pub borrow_count: usize, | ||||||
|  |     pub is_mutable: bool, | ||||||
|  |     pub identifier: Box<Identifier>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub struct ClosureParameters(pub Vec<ClosureParameter>); | ||||||
|  | 
 | ||||||
|  | impl ClosureParameters { | ||||||
|  |     pub fn is_empty(&self) -> bool { | ||||||
|  |         self.0.is_empty() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct ClosureParameter { | ||||||
|  |     pub identifier: Identifier, | ||||||
|  |     pub type_use: Option<TypeUse>, | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct ObjectAccess { | pub struct ObjectAccess { | ||||||
| @ -595,5 +645,47 @@ pub enum Literal { | |||||||
|     Double(f64), |     Double(f64), | ||||||
|     USize(usize), |     USize(usize), | ||||||
|     String(String), |     String(String), | ||||||
|  |     DString(DString), | ||||||
|  |     BacktickString(DString), | ||||||
|     Boolean(bool), |     Boolean(bool), | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct DString(pub Vec<DStringPart>); | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub enum DStringPart { | ||||||
|  |     String(String), | ||||||
|  |     Expression(Box<Expression>), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DStringPart { | ||||||
|  |     pub fn from_string(s: &str) -> DStringPart { | ||||||
|  |         DStringPart::String(String::from(s)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn from_expression(e: Expression) -> DStringPart { | ||||||
|  |         DStringPart::Expression(Box::new(e)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn is_string(&self) -> bool { | ||||||
|  |         match self { | ||||||
|  |             DStringPart::String(_) => true, | ||||||
|  |             _ => false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn unwrap_string(self) -> String { | ||||||
|  |         match self { | ||||||
|  |             DStringPart::String(s) => s, | ||||||
|  |             _ => panic!(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn is_expression(&self) -> bool { | ||||||
|  |         match self { | ||||||
|  |             DStringPart::Expression(_) => true, | ||||||
|  |             _ => false, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -19,6 +19,7 @@ impl PrettyPrint for Operator { | |||||||
| 
 | 
 | ||||||
| impl PrettyPrint for BinaryOperator { | impl PrettyPrint for BinaryOperator { | ||||||
|     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.write_indented("BinaryOperator(")?; | ||||||
|         use BinaryOperator::*; |         use BinaryOperator::*; | ||||||
|         match self { |         match self { | ||||||
|             Or => writer.write("||"), |             Or => writer.write("||"), | ||||||
| @ -36,12 +37,15 @@ impl PrettyPrint for BinaryOperator { | |||||||
|             Modulo => writer.write("%"), |             Modulo => writer.write("%"), | ||||||
|             LeftShift => writer.write("<<"), |             LeftShift => writer.write("<<"), | ||||||
|             RightShift => writer.write(">>"), |             RightShift => writer.write(">>"), | ||||||
|         } |         }?; | ||||||
|  |         writer.writeln(")")?; | ||||||
|  |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PrettyPrint for PrefixUnaryOperator { | impl PrettyPrint for PrefixUnaryOperator { | ||||||
|     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.write_indented("PrefixUnaryOperator(")?; | ||||||
|         use PrefixUnaryOperator::*; |         use PrefixUnaryOperator::*; | ||||||
|         match self { |         match self { | ||||||
|             Spread => writer.write("..."), |             Spread => writer.write("..."), | ||||||
| @ -50,17 +54,22 @@ impl PrettyPrint for PrefixUnaryOperator { | |||||||
|             Mut => writer.write("mut"), |             Mut => writer.write("mut"), | ||||||
|             Not => writer.write("!"), |             Not => writer.write("!"), | ||||||
|             Negative => writer.write("-"), |             Negative => writer.write("-"), | ||||||
|         } |         }?; | ||||||
|  |         writer.writeln(")")?; | ||||||
|  |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl PrettyPrint for SuffixUnaryOperator { | impl PrettyPrint for SuffixUnaryOperator { | ||||||
|     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.write_indented("SuffixUnaryOperator(")?; | ||||||
|         use SuffixUnaryOperator::*; |         use SuffixUnaryOperator::*; | ||||||
|         match self { |         match self { | ||||||
|             PlusPlus => writer.write("++"), |             PlusPlus => writer.write("++"), | ||||||
|             MinusMinus => writer.write("--"), |             MinusMinus => writer.write("--"), | ||||||
|         } |         }?; | ||||||
|  |         writer.write(")")?; | ||||||
|  |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -663,6 +672,7 @@ impl PrettyPrint for IfStatement { | |||||||
|     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|         writer.writeln_indented("IfStatement")?; |         writer.writeln_indented("IfStatement")?; | ||||||
|         writer.increase_indent(); |         writer.increase_indent(); | ||||||
|  |         writer.increase_indent(); | ||||||
|         self.condition.pretty_print(writer)?; |         self.condition.pretty_print(writer)?; | ||||||
|         self.then_block.pretty_print(writer)?; |         self.then_block.pretty_print(writer)?; | ||||||
|         writer.decrease_indent(); |         writer.decrease_indent(); | ||||||
| @ -674,13 +684,33 @@ impl PrettyPrint for IfElseStatement { | |||||||
|     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|         writer.writeln_indented("IfElseStatement")?; |         writer.writeln_indented("IfElseStatement")?; | ||||||
|         writer.increase_indent(); |         writer.increase_indent(); | ||||||
|         self.condition.pretty_print(writer)?; |         self.if_statement.pretty_print(writer)?; | ||||||
|         self.then_block.pretty_print(writer)?; |         self.else_ifs.pretty_print(writer)?; | ||||||
|         for else_if in &self.else_ifs.0 { |         if let Some(else_block) = &self.else_block { | ||||||
|             else_if.condition.pretty_print(writer)?; |             else_block.pretty_print(writer)?; | ||||||
|             else_if.then_block.pretty_print(writer)?; |  | ||||||
|         } |         } | ||||||
|         self.else_block.pretty_print(writer)?; |         writer.decrease_indent(); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for ElseIfs { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented("ElseIfs")?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         for if_statement in &self.0 { | ||||||
|  |             if_statement.pretty_print(writer)?; | ||||||
|  |         } | ||||||
|  |         writer.decrease_indent(); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for ElseBlock { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented("ElseBlock")?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         self.0.pretty_print(writer)?; | ||||||
|         writer.decrease_indent(); |         writer.decrease_indent(); | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @ -820,7 +850,88 @@ impl PrettyPrint for CallArgument { | |||||||
| 
 | 
 | ||||||
| impl PrettyPrint for Closure { | impl PrettyPrint for Closure { | ||||||
|     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|         todo!() |         writer.writeln_indented(&format!("Closure(is_move = {})", self.is_move))?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         if let Some(modifier) = &self.modifier { | ||||||
|  |             modifier.pretty_print(writer)?; | ||||||
|  |         } | ||||||
|  |         self.captures.pretty_print(writer)?; | ||||||
|  |         self.parameters.pretty_print(writer)?; | ||||||
|  |         if !self.statements.is_empty() { | ||||||
|  |             writer.writeln_indented("ClosureStatements")?; | ||||||
|  |             writer.increase_indent(); | ||||||
|  |             for statement in &self.statements { | ||||||
|  |                 statement.pretty_print(writer)?; | ||||||
|  |             } | ||||||
|  |             writer.decrease_indent(); | ||||||
|  |         } | ||||||
|  |         if let Some(expression) = &self.expression { | ||||||
|  |             expression.pretty_print(writer)?; | ||||||
|  |         } | ||||||
|  |         writer.decrease_indent(); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for ClosureModifier { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented(&format!( | ||||||
|  |             "ClosureModifier({})", | ||||||
|  |             match self { | ||||||
|  |                 ClosureModifier::Mut => "mut", | ||||||
|  |                 ClosureModifier::Cons => "cons", | ||||||
|  |             } | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for ClosureCaptures { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented("ClosureCaptures")?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         for capture in &self.0 { | ||||||
|  |             capture.pretty_print(writer)?; | ||||||
|  |         } | ||||||
|  |         writer.decrease_indent(); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for ClosureCapture { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented(&format!( | ||||||
|  |             "ClosureCapture(borrow_count = {}, is_mutable = {})", | ||||||
|  |             self.borrow_count, self.is_mutable | ||||||
|  |         ))?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         self.identifier.pretty_print(writer)?; | ||||||
|  |         writer.decrease_indent(); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for ClosureParameters { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented("ClosureParameters")?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         for parameter in &self.0 { | ||||||
|  |             parameter.pretty_print(writer)?; | ||||||
|  |         } | ||||||
|  |         writer.decrease_indent(); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for ClosureParameter { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented("ClosureParameter")?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         self.identifier.pretty_print(writer)?; | ||||||
|  |         if let Some(type_use) = &self.type_use { | ||||||
|  |             type_use.pretty_print(writer)?; | ||||||
|  |         } | ||||||
|  |         writer.decrease_indent(); | ||||||
|  |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -877,7 +988,31 @@ impl PrettyPrint for Literal { | |||||||
|             Double(d) => writer.writeln_indented(&format!("Double({})", d)), |             Double(d) => writer.writeln_indented(&format!("Double({})", d)), | ||||||
|             USize(u) => writer.writeln_indented(&format!("USize({})", u)), |             USize(u) => writer.writeln_indented(&format!("USize({})", u)), | ||||||
|             String(s) => writer.writeln_indented(&format!("String({})", s)), |             String(s) => writer.writeln_indented(&format!("String({})", s)), | ||||||
|  |             DString(d_string) => d_string.pretty_print(writer), | ||||||
|  |             BacktickString(d_string) => d_string.pretty_print(writer), | ||||||
|             Boolean(b) => writer.writeln_indented(&format!("Boolean({})", b)), |             Boolean(b) => writer.writeln_indented(&format!("Boolean({})", b)), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for DString { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented("DString")?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         for part in &self.0 { | ||||||
|  |             part.pretty_print(writer)?; | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PrettyPrint for DStringPart { | ||||||
|  |     fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.writeln_indented("DStringPart")?; | ||||||
|  |         writer.increase_indent(); | ||||||
|  |         match self { | ||||||
|  |             DStringPart::String(s) => writer.writeln_indented(&format!("String({})", s)), | ||||||
|  |             DStringPart::Expression(e) => e.pretty_print(writer), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -653,9 +653,7 @@ impl Unparse for FunctionBody { | |||||||
|             } |             } | ||||||
|             Block(body) => { |             Block(body) => { | ||||||
|                 writer.writeln("{")?; |                 writer.writeln("{")?; | ||||||
|                 writer.increase_indent(); |  | ||||||
|                 body.unparse_inner(writer)?; |                 body.unparse_inner(writer)?; | ||||||
|                 writer.decrease_indent(); |  | ||||||
|                 writer.writeln_indented("}") |                 writer.writeln_indented("}") | ||||||
|             } |             } | ||||||
|             Alias(identifier) => { |             Alias(identifier) => { | ||||||
| @ -826,9 +824,9 @@ impl Unparse for ReturnStatement { | |||||||
| 
 | 
 | ||||||
| impl Unparse for IfStatement { | impl Unparse for IfStatement { | ||||||
|     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|         writer.write_indented("if ")?; |         writer.write_indented("if (")?; | ||||||
|         self.condition.unparse(writer)?; |         self.condition.unparse(writer)?; | ||||||
|         writer.writeln(" {")?; |         writer.writeln(") {")?; | ||||||
|         self.then_block.unparse_inner(writer)?; |         self.then_block.unparse_inner(writer)?; | ||||||
|         writer.writeln_indented("}")?; |         writer.writeln_indented("}")?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
| @ -837,15 +835,11 @@ impl Unparse for IfStatement { | |||||||
| 
 | 
 | ||||||
| impl Unparse for IfElseStatement { | impl Unparse for IfElseStatement { | ||||||
|     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|         writer.write_indented("if ")?; |         self.if_statement.unparse(writer)?; | ||||||
|         self.condition.unparse(writer)?; |  | ||||||
|         writer.writeln(" {")?; |  | ||||||
|         self.then_block.unparse_inner(writer)?; |  | ||||||
|         writer.write_indented("} ")?; |  | ||||||
|         self.else_ifs.unparse(writer)?; |         self.else_ifs.unparse(writer)?; | ||||||
|         writer.writeln("else {")?; |         if let Some(else_block) = &self.else_block { | ||||||
|         self.else_block.unparse_inner(writer)?; |             else_block.unparse(writer)?; | ||||||
|         writer.writeln_indented("}")?; |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -853,16 +847,24 @@ impl Unparse for IfElseStatement { | |||||||
| impl Unparse for ElseIfs { | impl Unparse for ElseIfs { | ||||||
|     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|         for if_statement in &self.0 { |         for if_statement in &self.0 { | ||||||
|             writer.write(" else if ")?; |             writer.write_indented("else if (")?; | ||||||
|             if_statement.condition.unparse(writer)?; |             if_statement.condition.unparse(writer)?; | ||||||
|             writer.writeln(" {")?; |             writer.writeln(") {")?; | ||||||
|             if_statement.then_block.unparse_inner(writer)?; |             if_statement.then_block.unparse_inner(writer)?; | ||||||
|             writer.write_indented("} ")?; |             writer.writeln_indented("}")?; | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | impl Unparse for ElseBlock { | ||||||
|  |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.write_indented("else ")?; | ||||||
|  |         self.0.unparse(writer)?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| impl Unparse for WhileStatement { | impl Unparse for WhileStatement { | ||||||
|     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|         writer.write_indented("while ")?; |         writer.write_indented("while ")?; | ||||||
| @ -994,7 +996,100 @@ impl Unparse for CallArgument { | |||||||
| 
 | 
 | ||||||
| impl Unparse for Closure { | impl Unparse for Closure { | ||||||
|     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|         writer.write("{{ -> TODO }}") |         if let Some(modifier) = &self.modifier { | ||||||
|  |             match modifier { | ||||||
|  |                 ClosureModifier::Cons => writer.write("cons "), | ||||||
|  |                 ClosureModifier::Mut => writer.write("mut "), | ||||||
|  |             }?; | ||||||
|  |         } | ||||||
|  |         if self.is_move { | ||||||
|  |             writer.write("move ")?; | ||||||
|  |         } | ||||||
|  |         if !self.captures.is_empty() { | ||||||
|  |             self.captures.unparse(writer)?; | ||||||
|  |             writer.write(" ")?; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         writer.write("{ ")?; | ||||||
|  | 
 | ||||||
|  |         if !self.parameters.is_empty() { | ||||||
|  |             self.parameters.unparse(writer)?; | ||||||
|  |             writer.write(" -> ")?; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if !self.statements.is_empty() { | ||||||
|  |             writer.write("\n")?; | ||||||
|  |             writer.increase_indent(); | ||||||
|  |             for statement in &self.statements { | ||||||
|  |                 statement.unparse(writer)?; | ||||||
|  |             } | ||||||
|  |             writer.decrease_indent(); | ||||||
|  |         } | ||||||
|  |         if let Some(expression) = &self.expression { | ||||||
|  |             if self.statements.is_empty() { | ||||||
|  |                 expression.unparse(writer)?; | ||||||
|  |                 writer.write(" }")?; | ||||||
|  |             } else { | ||||||
|  |                 writer.increase_indent(); | ||||||
|  |                 writer.write_indented("")?; | ||||||
|  |                 expression.unparse(writer)?; | ||||||
|  |                 writer.write("\n")?; | ||||||
|  |                 writer.decrease_indent(); | ||||||
|  |                 writer.write_indented("}")?; | ||||||
|  |             } | ||||||
|  |         } else if !self.statements.is_empty() { | ||||||
|  |             writer.write_indented(" }")?; | ||||||
|  |         } else { | ||||||
|  |             writer.write(" }")?; | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Unparse for ClosureCaptures { | ||||||
|  |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.write("|")?; | ||||||
|  |         for (i, capture) in self.0.iter().enumerate() { | ||||||
|  |             capture.unparse(writer)?; | ||||||
|  |             if i != self.0.len() - 1 { | ||||||
|  |                 writer.write(", ")?; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         writer.write("|")?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Unparse for ClosureCapture { | ||||||
|  |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         for _ in 0..self.borrow_count { | ||||||
|  |             writer.write("&")?; | ||||||
|  |         } | ||||||
|  |         if self.is_mutable { | ||||||
|  |             writer.write("mut ")?; | ||||||
|  |         } | ||||||
|  |         self.identifier.unparse(writer)?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Unparse for ClosureParameters { | ||||||
|  |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         for parameter in &self.0 { | ||||||
|  |             parameter.unparse(writer)?; | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Unparse for ClosureParameter { | ||||||
|  |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         self.identifier.unparse(writer)?; | ||||||
|  |         if let Some(type_use) = &self.type_use { | ||||||
|  |             writer.write(": ")?; | ||||||
|  |             type_use.unparse(writer)?; | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1042,7 +1137,33 @@ impl Unparse for Literal { | |||||||
|             Double(d) => writer.write(&format!("{}", d)), |             Double(d) => writer.write(&format!("{}", d)), | ||||||
|             USize(u) => writer.write(&format!("{}", u)), |             USize(u) => writer.write(&format!("{}", u)), | ||||||
|             String(s) => writer.write(&format!("\"{}\"", s)), |             String(s) => writer.write(&format!("\"{}\"", s)), | ||||||
|  |             DString(d_string) => d_string.unparse(writer), | ||||||
|  |             BacktickString(d_string) => d_string.unparse(writer), | ||||||
|             Boolean(b) => writer.write(&format!("{}", b)), |             Boolean(b) => writer.write(&format!("{}", b)), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | impl Unparse for DString { | ||||||
|  |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         writer.write("\"")?; | ||||||
|  |         for part in &self.0 { | ||||||
|  |             part.unparse(writer)?; | ||||||
|  |         } | ||||||
|  |         writer.write("\"")?; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Unparse for DStringPart { | ||||||
|  |     fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||||
|  |         match self { | ||||||
|  |             DStringPart::String(s) => writer.write(s), | ||||||
|  |             DStringPart::Expression(e) => { | ||||||
|  |                 writer.write("${")?; | ||||||
|  |                 e.unparse(writer)?; | ||||||
|  |                 writer.write("}") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -2,7 +2,8 @@ use codespan_reporting::files::SimpleFiles; | |||||||
| use codespan_reporting::term; | use codespan_reporting::term; | ||||||
| use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; | use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; | ||||||
| use deimos::ast::build::build_ast; | use deimos::ast::build::build_ast; | ||||||
| use deimos::compile::name_analysis::{analyze_names, SymbolTable}; | use deimos::name_analysis::analyze_names; | ||||||
|  | use deimos::name_analysis::symbol_table::SymbolTable; | ||||||
| use deimos::parser::{DeimosParser, Rule}; | use deimos::parser::{DeimosParser, Rule}; | ||||||
| use pest::Parser; | use pest::Parser; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
| @ -16,7 +17,7 @@ pub fn name_analysis(path: &Path) -> Result<(), Box<dyn std::error::Error>> { | |||||||
|     match parse_result { |     match parse_result { | ||||||
|         Ok(mut pairs) => { |         Ok(mut pairs) => { | ||||||
|             let compilation_unit_pair = pairs.next().unwrap(); |             let compilation_unit_pair = pairs.next().unwrap(); | ||||||
|             let mut compilation_unit = build_ast(compilation_unit_pair); |             let mut compilation_unit = build_ast(file_id, compilation_unit_pair); | ||||||
|             let mut symbol_table = SymbolTable::new(); |             let mut symbol_table = SymbolTable::new(); | ||||||
|             let diagnostics = analyze_names(file_id, &mut compilation_unit, &mut symbol_table); |             let diagnostics = analyze_names(file_id, &mut compilation_unit, &mut symbol_table); | ||||||
|             if diagnostics.is_empty() { |             if diagnostics.is_empty() { | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ pub fn pretty_print_parse(path: &PathBuf) { | |||||||
|     match parse_result { |     match parse_result { | ||||||
|         Ok(mut pairs) => { |         Ok(mut pairs) => { | ||||||
|             let compilation_unit_pair = pairs.next().unwrap(); |             let compilation_unit_pair = pairs.next().unwrap(); | ||||||
|             let compilation_unit = build_ast(compilation_unit_pair); |             let compilation_unit = build_ast(0, compilation_unit_pair); | ||||||
|             let mut indent_writer = IndentWriter::new(0, "  ", Box::new(std::io::stdout())); |             let mut indent_writer = IndentWriter::new(0, "  ", Box::new(std::io::stdout())); | ||||||
|             compilation_unit |             compilation_unit | ||||||
|                 .pretty_print(&mut indent_writer) |                 .pretty_print(&mut indent_writer) | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ pub fn unparse(path: &PathBuf) { | |||||||
|     match parse_result { |     match parse_result { | ||||||
|         Ok(mut pairs) => { |         Ok(mut pairs) => { | ||||||
|             let compilation_unit_pair = pairs.next().unwrap(); |             let compilation_unit_pair = pairs.next().unwrap(); | ||||||
|             let compilation_unit = build_ast(compilation_unit_pair); |             let compilation_unit = build_ast(0, compilation_unit_pair); | ||||||
|             let mut writer = IndentWriter::new(0, "  ", Box::new(std::io::stdout())); |             let mut writer = IndentWriter::new(0, "  ", Box::new(std::io::stdout())); | ||||||
|             compilation_unit |             compilation_unit | ||||||
|                 .unparse(&mut writer) |                 .unparse(&mut writer) | ||||||
|  | |||||||
| @ -1 +0,0 @@ | |||||||
| pub mod name_analysis; |  | ||||||
| @ -1,540 +0,0 @@ | |||||||
| use crate::ast::named::Named; |  | ||||||
| use crate::ast::*; |  | ||||||
| use codespan_reporting::diagnostic::{Diagnostic, Label}; |  | ||||||
| use std::collections::HashMap; |  | ||||||
| use std::fmt::Display; |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Clone)] |  | ||||||
| pub enum Symbol { |  | ||||||
|     Function(FunctionSymbol), |  | ||||||
|     Variable(VariableSymbol), |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Symbol { |  | ||||||
|     pub fn name(&self) -> &str { |  | ||||||
|         match self { |  | ||||||
|             Symbol::Function(s) => s.declared_name.as_str(), |  | ||||||
|             Symbol::Variable(s) => s.name.as_str(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Display for Symbol { |  | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |  | ||||||
|         use Symbol::*; |  | ||||||
|         match self { |  | ||||||
|             Function(function_symbol) => write!(f, "{}", function_symbol), |  | ||||||
|             Variable(variable_symbol) => write!(f, "{}", variable_symbol), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Clone)] |  | ||||||
| pub struct FunctionSymbol { |  | ||||||
|     pub fqn: String, |  | ||||||
|     pub declared_name: String, |  | ||||||
|     pub is_public: bool, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Display for FunctionSymbol { |  | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |  | ||||||
|         write!( |  | ||||||
|             f, |  | ||||||
|             "FunctionSymbol(fqn = {}, declared_name = {}, is_public = {})", |  | ||||||
|             self.fqn, self.declared_name, self.is_public |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Clone)] |  | ||||||
| pub struct VariableSymbol { |  | ||||||
|     pub name: String, |  | ||||||
|     pub is_mutable: bool, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Display for VariableSymbol { |  | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |  | ||||||
|         write!( |  | ||||||
|             f, |  | ||||||
|             "VariableSymbol(name = {}, is_mutable = {})", |  | ||||||
|             self.name, self.is_mutable |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Default)] |  | ||||||
| pub struct Scope { |  | ||||||
|     parent: Option<usize>, |  | ||||||
|     symbols: HashMap<String, Symbol>, |  | ||||||
|     debug_name: String, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Default)] |  | ||||||
| pub struct SymbolTable { |  | ||||||
|     scopes: Vec<Scope>, |  | ||||||
|     current_scope_id: usize, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Contains a vec of scopes, like a flattened tree
 |  | ||||||
| impl SymbolTable { |  | ||||||
|     pub fn new() -> Self { |  | ||||||
|         let mut t = SymbolTable::default(); |  | ||||||
|         t.scopes.push(Scope::default()); |  | ||||||
|         t.current_scope_id = 0; |  | ||||||
|         t |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn current_scope_id(&self) -> usize { |  | ||||||
|         self.current_scope_id |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn push_scope(&mut self, debug_name: &str) { |  | ||||||
|         let id = self.scopes.len(); |  | ||||||
|         self.scopes.push(Scope { |  | ||||||
|             symbols: HashMap::new(), |  | ||||||
|             parent: Some(self.current_scope_id), |  | ||||||
|             debug_name: debug_name.to_string(), |  | ||||||
|         }); |  | ||||||
|         self.current_scope_id = id; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn pop_scope(&mut self) { |  | ||||||
|         if let Some(parent_id) = self.scopes[self.current_scope_id].parent { |  | ||||||
|             self.current_scope_id = parent_id; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn insert(&mut self, name: String, symbol: Symbol) -> Result<(), String> { |  | ||||||
|         if let Some(current_symbol) = self.scopes[self.current_scope_id].symbols.get(&name) { |  | ||||||
|             Err(format!( |  | ||||||
|                 "Symbol '{}' already defined in current scope.", |  | ||||||
|                 current_symbol.name() |  | ||||||
|             )) |  | ||||||
|         } else { |  | ||||||
|             self.scopes[self.current_scope_id] |  | ||||||
|                 .symbols |  | ||||||
|                 .insert(name, symbol); |  | ||||||
|             Ok(()) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn lookup(&self, name: &str, scope_id: usize) -> Result<&Symbol, String> { |  | ||||||
|         let mut scope_opt = Some(&self.scopes[scope_id]); |  | ||||||
|         while let Some(scope) = scope_opt { |  | ||||||
|             if let Some(symbol) = scope.symbols.get(name) { |  | ||||||
|                 return Ok(symbol); |  | ||||||
|             } |  | ||||||
|             scope_opt = if let Some(parent_id) = scope.parent { |  | ||||||
|                 Some(&self.scopes[parent_id]) |  | ||||||
|             } else { |  | ||||||
|                 None |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|         Err(format!("Symbol '{}' not found in current scope.", name)) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Display for SymbolTable { |  | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |  | ||||||
|         for (i, scope) in self.scopes.iter().enumerate() { |  | ||||||
|             writeln!(f, "Scope {} {}", i, scope.debug_name)?; |  | ||||||
|             for (name, symbol) in &scope.symbols { |  | ||||||
|                 writeln!(f, "  {}({})", name, symbol)?; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| struct FqnContext { |  | ||||||
|     stack: Vec<String>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl FqnContext { |  | ||||||
|     fn new() -> Self { |  | ||||||
|         FqnContext { stack: Vec::new() } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn push(&mut self, name: String) { |  | ||||||
|         self.stack.push(name); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn pop(&mut self) { |  | ||||||
|         self.stack.pop(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn current(&self) -> String { |  | ||||||
|         let mut acc = String::new(); |  | ||||||
|         for (i, name) in self.stack.iter().enumerate() { |  | ||||||
|             acc.push_str(name); |  | ||||||
|             if i != self.stack.len() - 1 { |  | ||||||
|                 acc.push_str("::") |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         acc |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn resolve(&self, name: &str) -> String { |  | ||||||
|         let mut acc = String::new(); |  | ||||||
|         if !self.stack.is_empty() { |  | ||||||
|             acc.push_str(&self.current()); |  | ||||||
|             acc.push_str("::"); |  | ||||||
|         } |  | ||||||
|         acc.push_str(name); |  | ||||||
|         acc |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn analyze_names( |  | ||||||
|     file_id: usize, |  | ||||||
|     compilation_unit: &mut CompilationUnit, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     let mut diagnostics = vec![]; |  | ||||||
| 
 |  | ||||||
|     let mut fqn_context = FqnContext::new(); |  | ||||||
|     if let Some(namespace) = &compilation_unit.namespace { |  | ||||||
|         fqn_context.push(namespace.name().to_string()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     for declaration in &mut compilation_unit.declarations { |  | ||||||
|         diagnostics.extend(gather_module_level_declaration( |  | ||||||
|             file_id, |  | ||||||
|             declaration, |  | ||||||
|             symbol_table, |  | ||||||
|             &mut fqn_context, |  | ||||||
|         )); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     assert_eq!(symbol_table.current_scope_id, 0); |  | ||||||
| 
 |  | ||||||
|     for declaration in &mut compilation_unit.declarations { |  | ||||||
|         diagnostics.extend(resolve_module_level_declaration(file_id, declaration, symbol_table)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     diagnostics |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_module_level_declaration( |  | ||||||
|     file_id: usize, |  | ||||||
|     declaration: &mut ModuleLevelDeclaration, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
|     fqn_context: &mut FqnContext, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     use ModuleLevelDeclaration::*; |  | ||||||
|     match declaration { |  | ||||||
|         Function(function_definition) => { |  | ||||||
|             gather_function_definition(file_id, function_definition, symbol_table, fqn_context) |  | ||||||
|         } |  | ||||||
|         _ => todo!(), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_function_definition( |  | ||||||
|     file_id: usize, |  | ||||||
|     function: &mut FunctionDefinition, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
|     fqn_context: &mut FqnContext, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     let mut diagnostics = vec![]; |  | ||||||
| 
 |  | ||||||
|     let declared_name = function.identifier.name().to_string(); |  | ||||||
|     let resolved_name = fqn_context.resolve(&declared_name); |  | ||||||
| 
 |  | ||||||
|     let insert_result = symbol_table.insert( |  | ||||||
|         declared_name.clone(), |  | ||||||
|         Symbol::Function(FunctionSymbol { |  | ||||||
|             fqn: resolved_name.clone(), |  | ||||||
|             declared_name, |  | ||||||
|             is_public: function.is_public, |  | ||||||
|         }), |  | ||||||
|     ); |  | ||||||
|     if let Err(msg) = insert_result { |  | ||||||
|         diagnostics.push( |  | ||||||
|             Diagnostic::error() |  | ||||||
|                 .with_message(msg) |  | ||||||
|                 .with_label(Label::primary(file_id, function.identifier.range)), |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function |  | ||||||
|         .identifier |  | ||||||
|         .set_scope_id(symbol_table.current_scope_id()); |  | ||||||
| 
 |  | ||||||
|     symbol_table.push_scope(&format!("FunctionScope({})", resolved_name)); |  | ||||||
|     // TODO: params
 |  | ||||||
|     diagnostics.extend(gather_function_body( |  | ||||||
|         file_id, |  | ||||||
|         &mut function.body, |  | ||||||
|         symbol_table, |  | ||||||
|         fqn_context, |  | ||||||
|     )); |  | ||||||
|     symbol_table.pop_scope(); |  | ||||||
| 
 |  | ||||||
|     diagnostics |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_function_body( |  | ||||||
|     file_id: usize, |  | ||||||
|     function_body: &mut FunctionBody, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
|     fqn_context: &mut FqnContext, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     use FunctionBody::*; |  | ||||||
|     match function_body { |  | ||||||
|         Block(block) => gather_block_statement(file_id, block, symbol_table, fqn_context), |  | ||||||
|         _ => todo!(), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_block_statement( |  | ||||||
|     file_id: usize, |  | ||||||
|     block: &mut BlockStatement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
|     fqn_context: &mut FqnContext, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     let mut diagnostics = vec![]; |  | ||||||
|     symbol_table.push_scope("BlockStatementScope"); |  | ||||||
|     for statement in &mut block.statements { |  | ||||||
|         diagnostics.extend(gather_statement( |  | ||||||
|             file_id, |  | ||||||
|             statement, |  | ||||||
|             symbol_table, |  | ||||||
|             fqn_context, |  | ||||||
|         )); |  | ||||||
|     } |  | ||||||
|     symbol_table.pop_scope(); |  | ||||||
|     diagnostics |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_statement( |  | ||||||
|     file_id: usize, |  | ||||||
|     statement: &mut Statement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
|     fqn_context: &mut FqnContext, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     use Statement::*; |  | ||||||
|     match statement { |  | ||||||
|         BlockStatement(block) => gather_block_statement(file_id, block, symbol_table, fqn_context), |  | ||||||
|         VariableDeclarationStatement(variable_declaration) => { |  | ||||||
|             gather_variable_declaration(file_id, variable_declaration, symbol_table) |  | ||||||
|         } |  | ||||||
|         AssignStatement(assign_statement) => { |  | ||||||
|             gather_assign_statement(assign_statement, symbol_table, fqn_context) |  | ||||||
|         } |  | ||||||
|         _ => todo!(), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_variable_declaration( |  | ||||||
|     file_id: usize, |  | ||||||
|     variable_declaration: &mut VariableDeclarationStatement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     let mut diagnostics = vec![]; |  | ||||||
|     let variable_name = variable_declaration.identifier.name().to_string(); |  | ||||||
| 
 |  | ||||||
|     let insert_result = symbol_table.insert( |  | ||||||
|         variable_name.clone(), |  | ||||||
|         Symbol::Variable(VariableSymbol { |  | ||||||
|             name: variable_name, |  | ||||||
|             is_mutable: variable_declaration.is_mutable, |  | ||||||
|         }), |  | ||||||
|     ); |  | ||||||
| 
 |  | ||||||
|     if let Err(msg) = insert_result { |  | ||||||
|         diagnostics.push( |  | ||||||
|             Diagnostic::error() |  | ||||||
|                 .with_message(msg) |  | ||||||
|                 .with_label(Label::primary( |  | ||||||
|                     file_id, |  | ||||||
|                     variable_declaration.identifier.range, |  | ||||||
|                 )), |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     variable_declaration |  | ||||||
|         .identifier |  | ||||||
|         .set_scope_id(symbol_table.current_scope_id()); |  | ||||||
| 
 |  | ||||||
|     diagnostics |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_assign_statement( |  | ||||||
|     assign_statement: &mut AssignStatement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
|     fqn_context: &mut FqnContext, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     let mut diagnostics = vec![]; |  | ||||||
|     diagnostics.extend(gather_expression( |  | ||||||
|         &mut assign_statement.lhs, |  | ||||||
|         symbol_table, |  | ||||||
|         fqn_context, |  | ||||||
|     )); |  | ||||||
|     diagnostics.extend(gather_expression( |  | ||||||
|         &mut assign_statement.rhs, |  | ||||||
|         symbol_table, |  | ||||||
|         fqn_context, |  | ||||||
|     )); |  | ||||||
|     diagnostics |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_expression( |  | ||||||
|     expression: &mut Expression, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
|     fqn_context: &mut FqnContext, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     use Expression::*; |  | ||||||
|     match expression { |  | ||||||
|         FullyQualifiedName(fully_qualified_name) => { |  | ||||||
|             gather_fully_qualified_name(fully_qualified_name, symbol_table) |  | ||||||
|         } |  | ||||||
|         _ => { |  | ||||||
|             vec![] |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn gather_fully_qualified_name( |  | ||||||
|     fully_qualified_name: &mut FullyQualifiedName, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     fully_qualified_name.set_scope_id(symbol_table.current_scope_id()); |  | ||||||
|     vec![] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /* Resolve */ |  | ||||||
| 
 |  | ||||||
| fn resolve_module_level_declaration( |  | ||||||
|     file_id: usize, |  | ||||||
|     declaration: &mut ModuleLevelDeclaration, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     use ModuleLevelDeclaration::*; |  | ||||||
|     match declaration { |  | ||||||
|         Function(function_definition) => { |  | ||||||
|             resolve_function_definition(file_id, function_definition, symbol_table) |  | ||||||
|         } |  | ||||||
|         _ => todo!(), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_function_definition( |  | ||||||
|     file_id: usize, |  | ||||||
|     function_definition: &mut FunctionDefinition, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     resolve_function_body(file_id, &mut function_definition.body, symbol_table) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_function_body( |  | ||||||
|     file_id: usize, |  | ||||||
|     function_body: &mut FunctionBody, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     use FunctionBody::*; |  | ||||||
|     match function_body { |  | ||||||
|         Block(block) => resolve_block(file_id, block, symbol_table), |  | ||||||
|         _ => todo!(), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_block( |  | ||||||
|     file_id: usize, |  | ||||||
|     block_statement: &mut BlockStatement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     block_statement |  | ||||||
|         .statements |  | ||||||
|         .iter_mut() |  | ||||||
|         .flat_map(|statement| resolve_statement(file_id, statement, symbol_table)) |  | ||||||
|         .collect() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_statement( |  | ||||||
|     file_id: usize, |  | ||||||
|     statement: &mut Statement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     use Statement::*; |  | ||||||
|     match statement { |  | ||||||
|         BlockStatement(block) => resolve_block(file_id, block, symbol_table), |  | ||||||
|         VariableDeclarationStatement(variable_declaration) => { |  | ||||||
|             resolve_variable_declaration(file_id, variable_declaration, symbol_table) |  | ||||||
|         } |  | ||||||
|         AssignStatement(assign_statement) => { |  | ||||||
|             resolve_assign_statement(file_id, assign_statement, symbol_table) |  | ||||||
|         } |  | ||||||
|         CallStatement(call_statement) => resolve_call_statement(file_id, call_statement, symbol_table), |  | ||||||
|         _ => todo!(), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_variable_declaration( |  | ||||||
|     file_id: usize, |  | ||||||
|     variable_declaration: &mut VariableDeclarationStatement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     if let Some(initializer) = &mut variable_declaration.initializer { |  | ||||||
|         resolve_expression(file_id, initializer, symbol_table) |  | ||||||
|     } else { |  | ||||||
|         vec![] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_assign_statement( |  | ||||||
|     file_id: usize, |  | ||||||
|     assign_statement: &mut AssignStatement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     let mut diagnostics = vec![]; |  | ||||||
|     diagnostics.extend(resolve_expression(file_id, &mut assign_statement.lhs, symbol_table)); |  | ||||||
|     diagnostics.extend(resolve_expression(file_id, &mut assign_statement.rhs, symbol_table)); |  | ||||||
|     diagnostics |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_call_statement( |  | ||||||
|     file_id: usize, |  | ||||||
|     call_statement: &mut CallStatement, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     resolve_expression(file_id, &mut call_statement.0, symbol_table) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_expression( |  | ||||||
|     file_id: usize, |  | ||||||
|     expression: &mut Expression, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     use Expression::*; |  | ||||||
|     match expression { |  | ||||||
|         FullyQualifiedName(fqn) => resolve_fully_qualified_name(file_id, fqn, symbol_table), |  | ||||||
|         Literal(_) => vec![], |  | ||||||
|         _ => todo!(), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn resolve_fully_qualified_name( |  | ||||||
|     file_id: usize, |  | ||||||
|     fully_qualified_name: &mut FullyQualifiedName, |  | ||||||
|     symbol_table: &mut SymbolTable, |  | ||||||
| ) -> Vec<Diagnostic<usize>> { |  | ||||||
|     let lookup_result = symbol_table.lookup( |  | ||||||
|         fully_qualified_name.name().as_ref(), |  | ||||||
|         fully_qualified_name.scope_id().expect(&format!( |  | ||||||
|             "FullyQualifiedName has no scope_id set: {:?}", |  | ||||||
|             fully_qualified_name |  | ||||||
|         )), |  | ||||||
|     ); |  | ||||||
|     match lookup_result { |  | ||||||
|         Ok(symbol) => { |  | ||||||
|             fully_qualified_name.set_symbol(symbol.clone()); |  | ||||||
|             vec![] |  | ||||||
|         } |  | ||||||
|         Err(e) => vec![ |  | ||||||
|             Diagnostic::error() |  | ||||||
|             .with_message(e) |  | ||||||
|                 .with_label(Label::primary(file_id, fully_qualified_name.range)) |  | ||||||
|         ], |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										3
									
								
								src/diagnostic/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/diagnostic/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | use codespan_reporting::diagnostic::Diagnostic; | ||||||
|  | 
 | ||||||
|  | pub type DmDiagnostic = Diagnostic<usize>; | ||||||
| @ -1,8 +1,9 @@ | |||||||
| #![feature(new_range_api)] | #![feature(new_range_api)] | ||||||
| #![allow(warnings)] | #![allow(warnings)] | ||||||
| pub mod ast; | pub mod ast; | ||||||
| pub mod compile; | pub mod diagnostic; | ||||||
| pub mod module; | pub mod module; | ||||||
|  | pub mod name_analysis; | ||||||
| pub mod object_file; | pub mod object_file; | ||||||
| pub mod parser; | pub mod parser; | ||||||
| pub mod util; | pub mod util; | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								src/name_analysis/fqn_context.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/name_analysis/fqn_context.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | pub(super) struct FqnContext { | ||||||
|  |     stack: Vec<String>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl FqnContext { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         FqnContext { stack: Vec::new() } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn push(&mut self, name: String) { | ||||||
|  |         self.stack.push(name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn pop(&mut self) { | ||||||
|  |         self.stack.pop(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn current(&self) -> String { | ||||||
|  |         let mut acc = String::new(); | ||||||
|  |         for (i, name) in self.stack.iter().enumerate() { | ||||||
|  |             acc.push_str(name); | ||||||
|  |             if i != self.stack.len() - 1 { | ||||||
|  |                 acc.push_str("::") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         acc | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn resolve(&self, name: &str) -> String { | ||||||
|  |         let mut acc = String::new(); | ||||||
|  |         if !self.stack.is_empty() { | ||||||
|  |             acc.push_str(&self.current()); | ||||||
|  |             acc.push_str("::"); | ||||||
|  |         } | ||||||
|  |         acc.push_str(name); | ||||||
|  |         acc | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										319
									
								
								src/name_analysis/gather.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								src/name_analysis/gather.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,319 @@ | |||||||
|  | use crate::ast::named::Named; | ||||||
|  | use crate::ast::*; | ||||||
|  | use crate::name_analysis::fqn_context::FqnContext; | ||||||
|  | use crate::name_analysis::symbol::{FunctionSymbol, ModuleSymbol, SourceDefinition, Symbol, VariableSymbol}; | ||||||
|  | use crate::name_analysis::symbol_table::{SymbolInsertError, SymbolTable}; | ||||||
|  | use crate::name_analysis::DiagnosticsContainer; | ||||||
|  | use codespan_reporting::diagnostic::{Diagnostic, Label}; | ||||||
|  | use std::range::Range; | ||||||
|  | 
 | ||||||
|  | fn handle_insert_error( | ||||||
|  |     err: SymbolInsertError, | ||||||
|  |     error_symbol_name: &str, | ||||||
|  |     error_file_id: usize, | ||||||
|  |     error_range: Range<usize>, | ||||||
|  |     symbol_types: &str, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     match err { | ||||||
|  |         SymbolInsertError::SymbolAlreadyDefined(s) => { | ||||||
|  |             let already_defined_definition = s.definition(); | ||||||
|  | 
 | ||||||
|  |             diagnostics.add( | ||||||
|  |                 Diagnostic::error() | ||||||
|  |                     .with_message(format!( | ||||||
|  |                         "{} symbol '{}' already defined in the current scope.", | ||||||
|  |                         symbol_types, error_symbol_name, | ||||||
|  |                     )) | ||||||
|  |                     .with_label( | ||||||
|  |                         Label::primary(error_file_id, error_range) | ||||||
|  |                             .with_message("Symbol duplicated here."), | ||||||
|  |                     ) | ||||||
|  |                     .with_label( | ||||||
|  |                         Label::secondary( | ||||||
|  |                             already_defined_definition.file_id(), | ||||||
|  |                             already_defined_definition.range(), | ||||||
|  |                         ) | ||||||
|  |                         .with_message("Symbol defined here."), | ||||||
|  |                     ), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(super) fn gather_module_level_declaration( | ||||||
|  |     declaration: &mut ModuleLevelDeclaration, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     use ModuleLevelDeclaration::*; | ||||||
|  |     match declaration { | ||||||
|  |         Module(module_declaration) => { | ||||||
|  |             gather_module_declaration(module_declaration, symbol_table, fqn_context, diagnostics); | ||||||
|  |         } | ||||||
|  |         Function(function_definition) => { | ||||||
|  |             gather_function_definition(function_definition, symbol_table, fqn_context, diagnostics) | ||||||
|  |         } | ||||||
|  |         _ => todo!(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_module_declaration( | ||||||
|  |     declaration: &mut ModuleDeclaration, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     // 1. Add mod identifier symbol
 | ||||||
|  |     // 2. Update fqn context
 | ||||||
|  |     // 3. Push scope
 | ||||||
|  |     // 4. Process declarations
 | ||||||
|  |     // 5. Pop scope
 | ||||||
|  | 
 | ||||||
|  |     let module_name = declaration.identifier.name(); | ||||||
|  | 
 | ||||||
|  |     let insert_result = symbol_table.insert( | ||||||
|  |         &module_name, | ||||||
|  |         Symbol::Module(ModuleSymbol::new( | ||||||
|  |             &fqn_context.resolve(&module_name), | ||||||
|  |             &module_name, | ||||||
|  |             declaration.is_public, | ||||||
|  |             &declaration.identifier, | ||||||
|  |         )), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     if let Err(err) = insert_result { | ||||||
|  |         handle_insert_error( | ||||||
|  |             err, | ||||||
|  |             &module_name, | ||||||
|  |             declaration.identifier.file_id, | ||||||
|  |             declaration.identifier.range, | ||||||
|  |             "module/type", | ||||||
|  |             diagnostics, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fqn_context.push(module_name.to_string()); | ||||||
|  | 
 | ||||||
|  |     symbol_table.push_scope(&format!("Module '{}' Scope", module_name)); | ||||||
|  |     for inner_declaration in &mut declaration.declarations { | ||||||
|  |         gather_module_level_declaration(inner_declaration, symbol_table, fqn_context, diagnostics); | ||||||
|  |     } | ||||||
|  |     symbol_table.pop_scope() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_function_definition( | ||||||
|  |     function: &mut FunctionDefinition, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     let declared_name = function.identifier.name(); | ||||||
|  |     let resolved_name = fqn_context.resolve(&declared_name); | ||||||
|  | 
 | ||||||
|  |     let insert_result = symbol_table.insert( | ||||||
|  |         &declared_name, | ||||||
|  |         Symbol::Function(FunctionSymbol::new( | ||||||
|  |             &resolved_name, | ||||||
|  |             &declared_name, | ||||||
|  |             function.is_public, | ||||||
|  |             &function.identifier, | ||||||
|  |         )), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     if let Err(err) = insert_result { | ||||||
|  |         handle_insert_error( | ||||||
|  |             err, | ||||||
|  |             &declared_name, | ||||||
|  |             function.identifier.file_id, | ||||||
|  |             function.identifier.range, | ||||||
|  |             "function/variable", | ||||||
|  |             diagnostics, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function | ||||||
|  |         .identifier | ||||||
|  |         .set_scope_id(symbol_table.current_scope_id()); | ||||||
|  | 
 | ||||||
|  |     symbol_table.push_scope(&format!("FunctionScope({})", resolved_name)); | ||||||
|  |     gather_parameters( | ||||||
|  |         &mut function.parameters, | ||||||
|  |         symbol_table, | ||||||
|  |         fqn_context, | ||||||
|  |         diagnostics, | ||||||
|  |     ); | ||||||
|  |     gather_function_body(&mut function.body, symbol_table, fqn_context, diagnostics); | ||||||
|  |     symbol_table.pop_scope(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_parameters( | ||||||
|  |     parameters: &mut Parameters, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     for parameter in &mut parameters.0 { | ||||||
|  |         gather_parameter(parameter, symbol_table, fqn_context, diagnostics); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_parameter( | ||||||
|  |     parameter: &mut Parameter, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     let parameter_name = parameter.identifier.name(); | ||||||
|  | 
 | ||||||
|  |     let insert_result = symbol_table.insert( | ||||||
|  |         ¶meter_name, | ||||||
|  |         Symbol::Variable(VariableSymbol::new( | ||||||
|  |             ¶meter_name, | ||||||
|  |             false, | ||||||
|  |             SourceDefinition::from_identifier(¶meter.identifier), | ||||||
|  |         )), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     if let Err(err) = insert_result { | ||||||
|  |         handle_insert_error( | ||||||
|  |             err, | ||||||
|  |             ¶meter_name, | ||||||
|  |             parameter.identifier.file_id, | ||||||
|  |             parameter.identifier.range, | ||||||
|  |             "function/variable", | ||||||
|  |             diagnostics, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     parameter | ||||||
|  |         .identifier | ||||||
|  |         .set_scope_id(symbol_table.current_scope_id()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_function_body( | ||||||
|  |     function_body: &mut FunctionBody, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     use crate::ast::FunctionBody::*; | ||||||
|  |     match function_body { | ||||||
|  |         Block(block) => gather_block_statement_inner(block, symbol_table, fqn_context, diagnostics), | ||||||
|  |         _ => todo!(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_block_statement( | ||||||
|  |     block: &mut BlockStatement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     symbol_table.push_scope("BlockStatementScope"); | ||||||
|  |     gather_block_statement_inner(block, symbol_table, fqn_context, diagnostics); | ||||||
|  |     symbol_table.pop_scope(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_block_statement_inner( | ||||||
|  |     block: &mut BlockStatement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     for statement in &mut block.statements { | ||||||
|  |         gather_statement(statement, symbol_table, fqn_context, diagnostics); | ||||||
|  |     } | ||||||
|  |     if let Some(expression) = &mut block.expression { | ||||||
|  |         gather_expression(expression, symbol_table, diagnostics); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_statement( | ||||||
|  |     statement: &mut Statement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     use crate::ast::Statement::*; | ||||||
|  |     match statement { | ||||||
|  |         BlockStatement(block) => { | ||||||
|  |             gather_block_statement(block, symbol_table, fqn_context, diagnostics) | ||||||
|  |         } | ||||||
|  |         VariableDeclarationStatement(variable_declaration) => { | ||||||
|  |             gather_variable_declaration(variable_declaration, symbol_table, diagnostics) | ||||||
|  |         } | ||||||
|  |         AssignStatement(assign_statement) => { | ||||||
|  |             gather_assign_statement(assign_statement, symbol_table, fqn_context, diagnostics) | ||||||
|  |         } | ||||||
|  |         _ => todo!(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_variable_declaration( | ||||||
|  |     variable_declaration: &mut VariableDeclarationStatement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     let variable_name = variable_declaration.identifier.name(); | ||||||
|  | 
 | ||||||
|  |     let insert_result = symbol_table.insert( | ||||||
|  |         &variable_name, | ||||||
|  |         Symbol::Variable(VariableSymbol::new( 
 | ||||||
|  |             &variable_name, | ||||||
|  |             variable_declaration.is_mutable, | ||||||
|  |             SourceDefinition::from_identifier(&variable_declaration.identifier), | ||||||
|  |         )), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     if let Err(err) = insert_result { | ||||||
|  |         handle_insert_error( | ||||||
|  |             err, | ||||||
|  |             &variable_name, | ||||||
|  |             variable_declaration.identifier.file_id, | ||||||
|  |             variable_declaration.identifier.range, | ||||||
|  |             "function/variable", | ||||||
|  |             diagnostics, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     variable_declaration | ||||||
|  |         .identifier | ||||||
|  |         .set_scope_id(symbol_table.current_scope_id()); | ||||||
|  | 
 | ||||||
|  |     if let Some(initializer) = &mut variable_declaration.initializer { | ||||||
|  |         gather_expression(initializer, symbol_table, diagnostics); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_assign_statement( | ||||||
|  |     assign_statement: &mut AssignStatement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     fqn_context: &mut FqnContext, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     gather_expression(&mut assign_statement.lhs, symbol_table, diagnostics); | ||||||
|  |     gather_expression(&mut assign_statement.rhs, symbol_table, diagnostics); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_expression( | ||||||
|  |     expression: &mut Expression, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     use crate::ast::Expression::*; | ||||||
|  |     match expression { | ||||||
|  |         FullyQualifiedName(fully_qualified_name) => { | ||||||
|  |             gather_fully_qualified_name(fully_qualified_name, symbol_table); | ||||||
|  |         } | ||||||
|  |         _ => {} | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn gather_fully_qualified_name( | ||||||
|  |     fully_qualified_name: &mut FullyQualifiedName, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  | ) { | ||||||
|  |     fully_qualified_name.set_scope_id(symbol_table.current_scope_id()); | ||||||
|  | } | ||||||
							
								
								
									
										128
									
								
								src/name_analysis/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/name_analysis/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,128 @@ | |||||||
|  | use crate::ast::named::Named; | ||||||
|  | use crate::ast::{CompilationUnit, Identifier}; | ||||||
|  | use crate::diagnostic::DmDiagnostic; | ||||||
|  | use crate::name_analysis::fqn_context::FqnContext; | ||||||
|  | use crate::name_analysis::gather::gather_module_level_declaration; | ||||||
|  | use crate::name_analysis::resolve::resolve_module_level_declaration; | ||||||
|  | use crate::name_analysis::symbol_table::SymbolTable; | ||||||
|  | use codespan_reporting::diagnostic::{Diagnostic, Label}; | ||||||
|  | use log::debug; | ||||||
|  | 
 | ||||||
|  | mod fqn_context; | ||||||
|  | mod gather; | ||||||
|  | mod resolve; | ||||||
|  | pub mod symbol; | ||||||
|  | pub mod symbol_table; | ||||||
|  | 
 | ||||||
|  | pub(self) struct DiagnosticsContainer { | ||||||
|  |     file_id: usize, | ||||||
|  |     diagnostics: Vec<DmDiagnostic>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DiagnosticsContainer { | ||||||
|  |     pub fn new(file_id: usize) -> DiagnosticsContainer { | ||||||
|  |         DiagnosticsContainer { | ||||||
|  |             file_id, | ||||||
|  |             diagnostics: vec![], | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn add(&mut self, diagnostic: DmDiagnostic) { | ||||||
|  |         self.diagnostics.push(diagnostic); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn add_error_at_identifier(&mut self, msg: &str, identifier: &Identifier) { | ||||||
|  |         self.diagnostics.push( | ||||||
|  |             Diagnostic::error() | ||||||
|  |                 .with_message(msg) | ||||||
|  |                 .with_label(Label::primary(self.file_id, identifier.range)), | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Into<Vec<DmDiagnostic>> for DiagnosticsContainer { | ||||||
|  |     fn into(self) -> Vec<DmDiagnostic> { | ||||||
|  |         self.diagnostics | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn analyze_names( | ||||||
|  |     file_id: usize, | ||||||
|  |     compilation_unit: &mut CompilationUnit, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  | ) -> Vec<DmDiagnostic> { | ||||||
|  |     let mut diagnostics = DiagnosticsContainer::new(file_id); | ||||||
|  | 
 | ||||||
|  |     let mut fqn_context = FqnContext::new(); | ||||||
|  |     if let Some(namespace) = &compilation_unit.namespace { | ||||||
|  |         fqn_context.push(namespace.name().to_string()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for declaration in &mut compilation_unit.declarations { | ||||||
|  |         gather_module_level_declaration( | ||||||
|  |             declaration, | ||||||
|  |             symbol_table, | ||||||
|  |             &mut fqn_context, | ||||||
|  |             &mut diagnostics, | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     assert_eq!(symbol_table.current_scope_id(), 0); | ||||||
|  | 
 | ||||||
|  |     for declaration in &mut compilation_unit.declarations { | ||||||
|  |         resolve_module_level_declaration(declaration, symbol_table, &mut diagnostics); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     diagnostics.into() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use super::*; | ||||||
|  |     use crate::ast::build::build_ast; | ||||||
|  |     use crate::parser::{DeimosParser, Rule}; | ||||||
|  |     use codespan_reporting::files::SimpleFiles; | ||||||
|  |     use codespan_reporting::term; | ||||||
|  |     use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; | ||||||
|  |     use pest::Parser; | ||||||
|  | 
 | ||||||
|  |     fn assert_no_diagnostics(src: &str) { | ||||||
|  |         let mut files = SimpleFiles::new(); | ||||||
|  |         let test_file_id = files.add("test.dm", src); | ||||||
|  | 
 | ||||||
|  |         let parse_result = DeimosParser::parse(Rule::CompilationUnit, src); | ||||||
|  |         if let Err(err) = &parse_result { | ||||||
|  |             panic!("{:?}", err); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let compilation_unit_pair = parse_result.unwrap().next().unwrap(); | ||||||
|  |         let mut ast = build_ast(test_file_id, compilation_unit_pair); | ||||||
|  | 
 | ||||||
|  |         let mut symbol_table = SymbolTable::new(); | ||||||
|  | 
 | ||||||
|  |         let diagnostics = analyze_names(test_file_id, &mut ast, &mut symbol_table); | ||||||
|  | 
 | ||||||
|  |         if !diagnostics.is_empty() { | ||||||
|  |             let writer = StandardStream::stderr(ColorChoice::Always); | ||||||
|  |             let config = term::Config::default(); | ||||||
|  | 
 | ||||||
|  |             for diagnostic in &diagnostics { | ||||||
|  |                 term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             eprintln!("{}", symbol_table); | ||||||
|  |             panic!("Diagnostics was not empty!"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn params_seen() { | ||||||
|  |         assert_no_diagnostics( | ||||||
|  |             r#" | ||||||
|  |             fn main(args: Array<String>) { | ||||||
|  |                 let x = args; | ||||||
|  |             } | ||||||
|  |             "#,
 | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										143
									
								
								src/name_analysis/resolve.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/name_analysis/resolve.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | |||||||
|  | use crate::ast::named::Named; | ||||||
|  | use crate::ast::*; | ||||||
|  | use crate::name_analysis::symbol_table::SymbolTable; | ||||||
|  | use crate::name_analysis::DiagnosticsContainer; | ||||||
|  | use codespan_reporting::diagnostic::{Diagnostic, Label}; | ||||||
|  | 
 | ||||||
|  | pub(super) fn resolve_module_level_declaration( | ||||||
|  |     declaration: &mut ModuleLevelDeclaration, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     use crate::ast::ModuleLevelDeclaration::*; | ||||||
|  |     match declaration { | ||||||
|  |         Function(function_definition) => { | ||||||
|  |             resolve_function_definition(function_definition, symbol_table, diagnostics) | ||||||
|  |         } | ||||||
|  |         _ => todo!(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_function_definition( | ||||||
|  |     function_definition: &mut FunctionDefinition, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     resolve_function_body(&mut function_definition.body, symbol_table, diagnostics); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_function_body( | ||||||
|  |     function_body: &mut FunctionBody, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     use crate::ast::FunctionBody::*; | ||||||
|  |     match function_body { | ||||||
|  |         Block(block) => resolve_block(block, symbol_table, diagnostics), | ||||||
|  |         _ => todo!(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_block( | ||||||
|  |     block_statement: &mut BlockStatement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     for statement in block_statement.statements.iter_mut() { | ||||||
|  |         resolve_statement(statement, symbol_table, diagnostics); | ||||||
|  |     } | ||||||
|  |     if let Some(expression) = block_statement.expression.as_mut() { | ||||||
|  |         resolve_expression(expression, symbol_table, diagnostics); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_statement( | ||||||
|  |     statement: &mut Statement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     use crate::ast::Statement::*; | ||||||
|  |     match statement { | ||||||
|  |         BlockStatement(block) => resolve_block(block, symbol_table, diagnostics), | ||||||
|  |         VariableDeclarationStatement(variable_declaration) => { | ||||||
|  |             resolve_variable_declaration(variable_declaration, symbol_table, diagnostics) | ||||||
|  |         } | ||||||
|  |         AssignStatement(assign_statement) => { | ||||||
|  |             resolve_assign_statement(assign_statement, symbol_table, diagnostics) | ||||||
|  |         } | ||||||
|  |         CallStatement(call_statement) => { | ||||||
|  |             resolve_call_statement(call_statement, symbol_table, diagnostics) | ||||||
|  |         } | ||||||
|  |         _ => todo!(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_variable_declaration( | ||||||
|  |     variable_declaration: &mut VariableDeclarationStatement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     if let Some(initializer) = &mut variable_declaration.initializer { | ||||||
|  |         resolve_expression(initializer, symbol_table, diagnostics) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_assign_statement( | ||||||
|  |     assign_statement: &mut AssignStatement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     resolve_expression(&mut assign_statement.lhs, symbol_table, diagnostics); | ||||||
|  |     resolve_expression(&mut assign_statement.rhs, symbol_table, diagnostics); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_call_statement( | ||||||
|  |     call_statement: &mut CallStatement, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     resolve_expression(&mut call_statement.0, symbol_table, diagnostics) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_expression( | ||||||
|  |     expression: &mut Expression, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     use crate::ast::Expression::*; | ||||||
|  |     match expression { | ||||||
|  |         FullyQualifiedName(fqn) => resolve_fully_qualified_name(fqn, symbol_table, diagnostics), | ||||||
|  |         Literal(_) => {} | ||||||
|  |         _ => todo!(), | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn resolve_fully_qualified_name( | ||||||
|  |     fully_qualified_name: &mut FullyQualifiedName, | ||||||
|  |     symbol_table: &mut SymbolTable, | ||||||
|  |     diagnostics: &mut DiagnosticsContainer, | ||||||
|  | ) { | ||||||
|  |     let lookup_result = symbol_table.lookup( | ||||||
|  |         fully_qualified_name.name().as_ref(), | ||||||
|  |         fully_qualified_name.scope_id().expect(&format!( | ||||||
|  |             "FullyQualifiedName has no scope_id set: {:?}", | ||||||
|  |             fully_qualified_name | ||||||
|  |         )), | ||||||
|  |     ); | ||||||
|  |     match lookup_result { | ||||||
|  |         Ok(symbol) => { | ||||||
|  |             fully_qualified_name.set_symbol(symbol.clone()); | ||||||
|  |         } | ||||||
|  |         Err(e) => diagnostics.add( | ||||||
|  |             Diagnostic::error() | ||||||
|  |                 .with_message(format!( | ||||||
|  |                     "No symbol with name '{}' found in current scope.", | ||||||
|  |                     fully_qualified_name.name() | ||||||
|  |                 )) | ||||||
|  |                 .with_label(Label::primary( | ||||||
|  |                     fully_qualified_name.file_id, | ||||||
|  |                     fully_qualified_name.range, | ||||||
|  |                 )), | ||||||
|  |         ), | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										169
									
								
								src/name_analysis/symbol.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/name_analysis/symbol.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,169 @@ | |||||||
|  | use crate::ast::Identifier; | ||||||
|  | use std::fmt::Display; | ||||||
|  | use std::range::Range; | ||||||
|  | 
 | ||||||
|  | #[derive(Clone, Debug)] | ||||||
|  | pub struct SourceDefinition { | ||||||
|  |     file_id: usize, | ||||||
|  |     range: Range<usize>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl SourceDefinition { | ||||||
|  |     pub fn from_identifier(identifier: &Identifier) -> Self { | ||||||
|  |         SourceDefinition { | ||||||
|  |             file_id: identifier.file_id, | ||||||
|  |             range: identifier.range, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn file_id(&self) -> usize { | ||||||
|  |         self.file_id | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn range(&self) -> Range<usize> { | ||||||
|  |         self.range.clone() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub enum Symbol { | ||||||
|  |     Function(FunctionSymbol), | ||||||
|  |     Variable(VariableSymbol), | ||||||
|  |     Module(ModuleSymbol), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Symbol { | ||||||
|  |     pub fn name(&self) -> &str { | ||||||
|  |         match self { | ||||||
|  |             Symbol::Function(s) => s.declared_name.as_str(), | ||||||
|  |             Symbol::Variable(s) => s.name.as_str(), | ||||||
|  |             Symbol::Module(s) => s.declared_name.as_str(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn definition(&self) -> &SourceDefinition { | ||||||
|  |         match self { | ||||||
|  |             Symbol::Function(s) => s.definition(), | ||||||
|  |             Symbol::Module(s) => s.definition(), | ||||||
|  |             Symbol::Variable(s) => s.definition(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for Symbol { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||||
|  |         use Symbol::*; | ||||||
|  |         match self { | ||||||
|  |             Function(function_symbol) => write!(f, "{}", function_symbol), | ||||||
|  |             Variable(variable_symbol) => write!(f, "{}", variable_symbol), | ||||||
|  |             Module(module_symbol) => write!(f, "{}", module_symbol), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct FunctionSymbol { | ||||||
|  |     pub fqn: String, | ||||||
|  |     pub declared_name: String, | ||||||
|  |     pub is_public: bool, | ||||||
|  |     definition: SourceDefinition, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl FunctionSymbol { | ||||||
|  |     pub fn new( | ||||||
|  |         fqn: &str, | ||||||
|  |         declared_name: &str, | ||||||
|  |         is_public: bool, | ||||||
|  |         identifier: &Identifier, | ||||||
|  |     ) -> FunctionSymbol { | ||||||
|  |         FunctionSymbol { | ||||||
|  |             fqn: fqn.to_string(), | ||||||
|  |             declared_name: declared_name.to_string(), | ||||||
|  |             is_public, | ||||||
|  |             definition: SourceDefinition::from_identifier(identifier), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn definition(&self) -> &SourceDefinition { | ||||||
|  |         &self.definition | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for FunctionSymbol { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||||
|  |         write!( | ||||||
|  |             f, | ||||||
|  |             "FunctionSymbol(fqn = {}, declared_name = {}, is_public = {})", | ||||||
|  |             self.fqn, self.declared_name, self.is_public | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct VariableSymbol { | ||||||
|  |     pub name: String, | ||||||
|  |     pub is_mutable: bool, | ||||||
|  |     definition: SourceDefinition, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl VariableSymbol { | ||||||
|  |     pub fn new(name: &str, is_mutable: bool, definition: SourceDefinition) -> Self { | ||||||
|  |         VariableSymbol { | ||||||
|  |             name: name.to_string(), | ||||||
|  |             is_mutable, | ||||||
|  |             definition, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn definition(&self) -> &SourceDefinition { | ||||||
|  |         &self.definition | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for VariableSymbol { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||||
|  |         write!( | ||||||
|  |             f, | ||||||
|  |             "VariableSymbol(name = {}, is_mutable = {})", | ||||||
|  |             self.name, self.is_mutable | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct ModuleSymbol { | ||||||
|  |     pub fqn: String, | ||||||
|  |     pub declared_name: String, | ||||||
|  |     pub is_public: bool, | ||||||
|  |     definition: SourceDefinition, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl ModuleSymbol { | ||||||
|  |     pub fn new( | ||||||
|  |         fqn: &str, | ||||||
|  |         declared_name: &str, | ||||||
|  |         is_public: bool, | ||||||
|  |         identifier: &Identifier, | ||||||
|  |     ) -> ModuleSymbol { | ||||||
|  |         ModuleSymbol { | ||||||
|  |             fqn: fqn.to_string(), | ||||||
|  |             declared_name: declared_name.to_string(), | ||||||
|  |             is_public, | ||||||
|  |             definition: SourceDefinition::from_identifier(identifier), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn definition(&self) -> &SourceDefinition { | ||||||
|  |         &self.definition | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for ModuleSymbol { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||||
|  |         write!( | ||||||
|  |             f, | ||||||
|  |             "ModuleSymbol(name = {}, is_public = {})", | ||||||
|  |             self.fqn, self.is_public | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										129
									
								
								src/name_analysis/symbol_table.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/name_analysis/symbol_table.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,129 @@ | |||||||
|  | use crate::name_analysis::symbol::Symbol; | ||||||
|  | use std::collections::HashMap; | ||||||
|  | use std::fmt::Display; | ||||||
|  | use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined; | ||||||
|  | use crate::name_analysis::symbol_table::SymbolLookupError::NoDefinition; | ||||||
|  | 
 | ||||||
|  | pub struct Scope { | ||||||
|  |     parent: Option<usize>, | ||||||
|  |     function_and_variable_symbols: HashMap<String, Symbol>, | ||||||
|  |     type_and_module_symbols: HashMap<String, Symbol>, | ||||||
|  |     debug_name: String, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Scope { | ||||||
|  |     pub fn new(parent: Option<usize>, debug_name: String) -> Scope { | ||||||
|  |         Scope { | ||||||
|  |             parent, | ||||||
|  |             function_and_variable_symbols: HashMap::new(), | ||||||
|  |             type_and_module_symbols: HashMap::new(), | ||||||
|  |             debug_name, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub enum SymbolInsertError { | ||||||
|  |     SymbolAlreadyDefined(Symbol), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub enum SymbolLookupError { | ||||||
|  |     NoDefinition | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct SymbolTable { | ||||||
|  |     scopes: Vec<Scope>, | ||||||
|  |     current_scope_id: usize, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Contains a vec of scopes, like a flattened tree
 | ||||||
|  | impl SymbolTable { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         let mut t = SymbolTable { | ||||||
|  |             scopes: vec![Scope::new(None, String::from("GlobalScope"))], | ||||||
|  |             current_scope_id: 0, | ||||||
|  |         }; | ||||||
|  |         t | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn current_scope_id(&self) -> usize { | ||||||
|  |         self.current_scope_id | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn push_scope(&mut self, debug_name: &str) { | ||||||
|  |         let id = self.scopes.len(); | ||||||
|  |         self.scopes | ||||||
|  |             .push(Scope::new(Some(self.current_scope_id), debug_name.to_string())); | ||||||
|  |         self.current_scope_id = id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn pop_scope(&mut self) { | ||||||
|  |         if let Some(parent_id) = self.scopes[self.current_scope_id].parent { | ||||||
|  |             self.current_scope_id = parent_id; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn insert(&mut self, name: &str, symbol: Symbol) -> Result<(), SymbolInsertError> { | ||||||
|  |         match symbol { | ||||||
|  |             Symbol::Function(_) | Symbol::Variable(_) => { | ||||||
|  |                 self.insert_function_or_variable(name, symbol) | ||||||
|  |             } | ||||||
|  |             Symbol::Module(_) => self.insert_module_or_type(name, symbol), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn insert_function_or_variable(&mut self, name: &str, symbol: Symbol) -> Result<(), SymbolInsertError> { | ||||||
|  |         if let Some(defined_symbol) = self.scopes[self.current_scope_id] | ||||||
|  |             .function_and_variable_symbols | ||||||
|  |             .get(name) | ||||||
|  |         { | ||||||
|  |             Err(SymbolAlreadyDefined(defined_symbol.clone())) | ||||||
|  |         } else { | ||||||
|  |             self.scopes[self.current_scope_id] | ||||||
|  |                 .function_and_variable_symbols | ||||||
|  |                 .insert(name.to_string(), symbol); | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn insert_module_or_type(&mut self, name: &str, symbol: Symbol) -> Result<(), SymbolInsertError> { | ||||||
|  |         if let Some(defined_symbol) = self.scopes[self.current_scope_id] | ||||||
|  |             .type_and_module_symbols | ||||||
|  |             .get(name) | ||||||
|  |         { | ||||||
|  |             Err(SymbolAlreadyDefined(defined_symbol.clone())) | ||||||
|  |         } else { | ||||||
|  |             self.scopes[self.current_scope_id] | ||||||
|  |                 .type_and_module_symbols | ||||||
|  |                 .insert(name.to_string(), symbol); | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn lookup(&self, name: &str, scope_id: usize) -> Result<&Symbol, SymbolLookupError> { | ||||||
|  |         let mut scope_opt = Some(&self.scopes[scope_id]); | ||||||
|  |         while let Some(scope) = scope_opt { | ||||||
|  |             if let Some(symbol) = scope.function_and_variable_symbols.get(name) { | ||||||
|  |                 return Ok(symbol); | ||||||
|  |             } | ||||||
|  |             scope_opt = if let Some(parent_id) = scope.parent { | ||||||
|  |                 Some(&self.scopes[parent_id]) | ||||||
|  |             } else { | ||||||
|  |                 None | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         Err(NoDefinition) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for SymbolTable { | ||||||
|  |     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||||
|  |         writeln!(f, "SymbolTable(current_scope = {})", self.current_scope_id)?; | ||||||
|  |         for (i, scope) in self.scopes.iter().enumerate() { | ||||||
|  |             writeln!(f, "Scope {} {}", i, scope.debug_name)?; | ||||||
|  |             for (name, symbol) in &scope.function_and_variable_symbols { | ||||||
|  |                 writeln!(f, "  {}({})", name, symbol)?; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -95,6 +95,7 @@ Borrow = { "&" } | |||||||
| Star = { "*" } | Star = { "*" } | ||||||
| LeftShift = { "<<" } | LeftShift = { "<<" } | ||||||
| RightShift = { ">>" } | RightShift = { ">>" } | ||||||
|  | Index = { "[]" } | ||||||
| 
 | 
 | ||||||
| Operator = { | Operator = { | ||||||
|       Or |       Or | ||||||
| @ -118,6 +119,9 @@ Operator = { | |||||||
|     | Spread |     | Spread | ||||||
|     | Borrow |     | Borrow | ||||||
|     | Star |     | Star | ||||||
|  |     | LeftShift | ||||||
|  |     | RightShift | ||||||
|  |     | Index | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Names | // Names | ||||||
| @ -429,9 +433,9 @@ FunctionModifier = { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FunctionBody = { | FunctionBody = { | ||||||
|       FunctionEqualsBody |       FunctionAliasBody | ||||||
|  |     | FunctionEqualsBody | ||||||
|     | BlockStatement |     | BlockStatement | ||||||
|     | FunctionAliasBody |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FunctionEqualsBody = { | FunctionEqualsBody = { | ||||||
| @ -484,17 +488,19 @@ BlockStatement = { | |||||||
| 
 | 
 | ||||||
| Statement = { | Statement = { | ||||||
|       ( |       ( | ||||||
|           BlockStatement |           VariableDeclaration | ||||||
|         | VariableDeclaration |  | ||||||
|         | AssignmentStatement |         | AssignmentStatement | ||||||
|         | CallStatement |         | CallStatement | ||||||
|         | ReturnStatement |         | ReturnStatement | ||||||
|         | IfStatement |       ) | ||||||
|  |     ~ Semicolon | ||||||
|  |     | ( | ||||||
|  |           BlockStatement | ||||||
|         | IfElseStatement |         | IfElseStatement | ||||||
|  |         | IfStatement | ||||||
|         | WhileStatement |         | WhileStatement | ||||||
|         | ForStatement |         | ForStatement | ||||||
|       )? |       ) | ||||||
|     ~ Semicolon |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| VariableDeclaration = { | VariableDeclaration = { | ||||||
| @ -530,30 +536,43 @@ ReturnStatement = { | |||||||
| 
 | 
 | ||||||
| IfStatement = { | IfStatement = { | ||||||
|       If |       If | ||||||
|  |     ~ "(" | ||||||
|     ~ Expression |     ~ Expression | ||||||
|  |     ~ ")" | ||||||
|     ~ BlockStatement |     ~ BlockStatement | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IfElseStatement = { | IfElseStatement = { | ||||||
|       If |       IfStatement | ||||||
|     ~ Expression |     ~ ElseIf* | ||||||
|     ~ BlockStatement |     ~ ElseBlock? | ||||||
|     ~ ( Else ~ IfStatement )* | } | ||||||
|     ~ Else | 
 | ||||||
|  | ElseIf = { | ||||||
|  |       Else | ||||||
|  |     ~ IfStatement | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ElseBlock = { | ||||||
|  |       Else | ||||||
|     ~ BlockStatement |     ~ BlockStatement | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| WhileStatement = { | WhileStatement = { | ||||||
|       While |       While | ||||||
|  |     ~ "(" | ||||||
|     ~ Expression |     ~ Expression | ||||||
|  |     ~ ")" | ||||||
|     ~ BlockStatement |     ~ BlockStatement | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ForStatement = { | ForStatement = { | ||||||
|       For |       For | ||||||
|     ~ Expression |     ~ "(" | ||||||
|  |     ~ Identifier | ||||||
|     ~ In |     ~ In | ||||||
|     ~ Expression |     ~ Expression | ||||||
|  |     ~ ")" | ||||||
|     ~ BlockStatement |     ~ BlockStatement | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -652,9 +671,18 @@ ParenthesizedExpression = { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ObjectAccess = { | ObjectAccess = { | ||||||
|  |       ( ObjectProperty | ObjectIndex )+ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ObjectProperty = { | ||||||
|       "." |       "." | ||||||
|     ~ Identifier |     ~ Identifier | ||||||
|     ~ ( "." ~ Identifier )* | } | ||||||
|  | 
 | ||||||
|  | ObjectIndex = { | ||||||
|  |       "[" | ||||||
|  |     ~ Expression | ||||||
|  |     ~ "]" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Calls | // Calls | ||||||
| @ -708,10 +736,8 @@ ClosureCaptures = { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ClosureCapture = { | ClosureCapture = { | ||||||
|       ( |       Borrow* | ||||||
|           Mut |     ~ Mut? | ||||||
|         | ( Borrow ~ Mut? )* |  | ||||||
|       )? |  | ||||||
|     ~ Identifier |     ~ Identifier | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -782,14 +808,15 @@ StringChar = { | |||||||
| DStringInner = @{ DStringChar+ } | DStringInner = @{ DStringChar+ } | ||||||
| 
 | 
 | ||||||
| DStringChar = { | DStringChar = { | ||||||
|       !( "\"" | "\\" ) ~ ANY |       !( "\"" | "\\" | "${" ) ~ ANY | ||||||
|     | "\\" ~ ( "\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "$" ) |     | "\\" ~ ( "\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "$" ) | ||||||
|     | "\\" ~ ( "u" ~ ASCII_HEX_DIGIT{4} ) |     | "\\" ~ ( "u" ~ ASCII_HEX_DIGIT{4} ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| DStringExpression = { | DStringExpression = { | ||||||
|       "$" |       "${" | ||||||
|     ~ BlockStatement |     ~ Expression | ||||||
|  |     ~ "}" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BacktickString = { | BacktickString = { | ||||||
| @ -802,7 +829,7 @@ BacktickString = { | |||||||
| BacktickInner = @{ BacktickStringChar+ } | BacktickInner = @{ BacktickStringChar+ } | ||||||
| 
 | 
 | ||||||
| BacktickStringChar = { | BacktickStringChar = { | ||||||
|       !( "\\`" | "\\" ) ~ ANY |       !( "\\`" | "\\" | "${" ) ~ ANY | ||||||
|     | "\\" ~ ( "`" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "$" ) |     | "\\" ~ ( "`" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "$" ) | ||||||
|     | "\\" ~ ( "u" ~ ASCII_HEX_DIGIT{4} ) |     | "\\" ~ ( "u" ~ ASCII_HEX_DIGIT{4} ) | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,8 +7,8 @@ pub struct DeimosParser; | |||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod deimos_parser_tests { | mod deimos_parser_tests { | ||||||
|     use crate::parser::{DeimosParser, Rule}; |     use crate::parser::{DeimosParser, Rule}; | ||||||
|     use pest::iterators::Pair; |     use indoc::indoc; | ||||||
|     use pest::{Parser}; |     use pest::{parses_to, Parser}; | ||||||
| 
 | 
 | ||||||
|     macro_rules! fail_rule { |     macro_rules! fail_rule { | ||||||
|         ($pair: expr; $rule:path) => {{ |         ($pair: expr; $rule:path) => {{ | ||||||
| @ -28,70 +28,46 @@ mod deimos_parser_tests { | |||||||
|         }}; |         }}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     macro_rules! match_inner_rules { |     fn parses_to(rule: Rule, input: &str) { | ||||||
|         ($pair:expr; $rule:path; $handle_last:expr) => {{ |         let parse_result = DeimosParser::parse(rule, input); | ||||||
|             if let $rule = $pair.as_rule() { |         if let Err(e) = parse_result { | ||||||
|                 $handle_last($pair); |             panic!("Parsing failed.\n{}", e); | ||||||
|         } else { |         } else { | ||||||
|                 fail_rule!($pair; $rule); |             let mut pairs = parse_result.unwrap(); | ||||||
|             } |             if input.trim() != pairs.as_str().trim() { | ||||||
|         }}; |                 panic!( | ||||||
|         ($pair:expr; $head_rule:path $(, $tail_rules:path )* ; $handle_last:expr) => {{ |                     "Parsing did not consume entire input. Consumed only:\n{}", | ||||||
|             if let $head_rule = $pair.as_rule() { |                     pairs.as_str() | ||||||
|                 let inner = $pair.into_inner().next().unwrap(); |  | ||||||
|                 match_inner_rules!( |  | ||||||
|                     inner; |  | ||||||
|                     $( $tail_rules ),*; |  | ||||||
|                     $handle_last |  | ||||||
|                 ); |                 ); | ||||||
|             } else { |  | ||||||
|                 fail_rule!($pair; $head_rule); |  | ||||||
|             } |             } | ||||||
|         }}; |             let first = pairs.next().unwrap(); | ||||||
|  |             match_rule!(first; rule) | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|     fn parse(rule: Rule, input: &str) -> Pair<Rule> { |  | ||||||
|         let pair = DeimosParser::parse(rule, input) |  | ||||||
|             .expect("Parsing failed.") |  | ||||||
|             .next() |  | ||||||
|             .unwrap(); |  | ||||||
|         dbg!(&pair); |  | ||||||
|         pair |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn hex_int() { |     fn hex_int() { | ||||||
|         let pair = parse(Rule::IntLiteral, "0x1234abcd"); |         parses_to(Rule::IntLiteral, "0x1234abcd"); | ||||||
|         match_inner_rules!(pair; Rule::IntLiteral, Rule::NumberBase, Rule::HexadecimalBase; |hexadecimal_base: Pair<Rule>| { |  | ||||||
|             assert_eq!(hexadecimal_base.as_str(), "0x1234abcd") |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn hex_long() { |     fn hex_long() { | ||||||
|         let pair = parse(Rule::LongLiteral, "0x123456789abcdefL"); |         parses_to(Rule::LongLiteral, "0x123456789abcdefL"); | ||||||
|         match_inner_rules!(pair; Rule::LongLiteral, Rule::NumberBase, Rule::HexadecimalBase; |hexadecimal_base: Pair<Rule>| { |  | ||||||
|             assert_eq!(hexadecimal_base.as_str(), "0x123456789abcdef") |  | ||||||
|         }) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn identifier_call_as_expression() { |     fn suffix_expression_call_single_identifier() { | ||||||
|         parse(Rule::Expression, "foo()"); |         parses_to(Rule::SuffixExpression, "foo()"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     mod smoke_screen_tests { |  | ||||||
|         use crate::parser::deimos_parser_tests::parse; |  | ||||||
|         use crate::parser::Rule; |  | ||||||
| 
 |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn simple_interface() { |     fn simple_interface() { | ||||||
|             parse(Rule::CompilationUnit, "pub int Simple { fn foo() -> Void }"); |         parses_to(Rule::CompilationUnit, "pub int Simple { fn foo() -> Void }"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn interface_with_op() { |     fn interface_with_op() { | ||||||
|             parse( |         parses_to( | ||||||
|             Rule::CompilationUnit, |             Rule::CompilationUnit, | ||||||
|             "pub int Callable { op () () -> Void }", |             "pub int Callable { op () () -> Void }", | ||||||
|         ); |         ); | ||||||
| @ -99,10 +75,109 @@ mod deimos_parser_tests { | |||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn interface_with_alias() { |     fn interface_with_alias() { | ||||||
|             parse( |         parses_to( | ||||||
|             Rule::CompilationUnit, |             Rule::CompilationUnit, | ||||||
|                 "pub int Callable {\n  fn call() -> Void\n  op () () -> Void alias call\n}", |             indoc! {" | ||||||
|  |                 pub int Callable { | ||||||
|  |                     fn call() -> Void | ||||||
|  |                     def op () () -> Void alias call | ||||||
|  |                 } | ||||||
|  |             "},
 | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn index_identifier() { | ||||||
|  |         parses_to(Rule::SuffixExpression, "foo[0]"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn chained_index_call_on_identifier() { | ||||||
|  |         parses_to(Rule::SuffixExpression, "foo[0]()"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn if_statement() { | ||||||
|  |         parses_to( | ||||||
|  |             Rule::IfStatement, | ||||||
|  |             indoc! {" | ||||||
|  |             if (foo == 42) { | ||||||
|  |                 bar() | ||||||
|  |             }"},
 | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn if_else_statement() { | ||||||
|  |         parses_to( | ||||||
|  |             Rule::IfElseStatement, | ||||||
|  |             indoc! {" | ||||||
|  |             if (foo == 42) { | ||||||
|  |                 bar() | ||||||
|  |             } else { | ||||||
|  |                 baz() | ||||||
|  |             }"},
 | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn if_else_if_statement() { | ||||||
|  |         parses_to( | ||||||
|  |             Rule::IfElseStatement, | ||||||
|  |             indoc! {" | ||||||
|  |             if (foo == 42) { | ||||||
|  |                 bar() | ||||||
|  |             } else if (foo == 16) { | ||||||
|  |                 baz() | ||||||
|  |             }"},
 | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn if_else_if_else_statement() { | ||||||
|  |         parses_to( | ||||||
|  |             Rule::IfElseStatement, | ||||||
|  |             indoc! {" | ||||||
|  |             if (foo == 42) { | ||||||
|  |                 foo() | ||||||
|  |             } else if (foo == 16) { | ||||||
|  |                 baz() | ||||||
|  |             } else { | ||||||
|  |                 fizz() | ||||||
|  |             }"},
 | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |     
 | ||||||
|  |     #[test] | ||||||
|  |     fn while_statement() { | ||||||
|  |         parses_to(Rule::WhileStatement, "while (foo) { bar() }"); | ||||||
|  |     } | ||||||
|  |     
 | ||||||
|  |     #[test] | ||||||
|  |     fn for_statement() { | ||||||
|  |         parses_to(Rule::ForStatement, "for (foo in bar) { baz(foo); }"); | ||||||
|  |     } | ||||||
|  |     
 | ||||||
|  |     #[test] | ||||||
|  |     fn if_statement_with_call_condition() { | ||||||
|  |         parses_to(Rule::IfStatement, indoc! {" | ||||||
|  |             if (foo()) { | ||||||
|  |                 bar() | ||||||
|  |             } | ||||||
|  |         "})
 | ||||||
|  |     } | ||||||
|  |     
 | ||||||
|  |     #[test] | ||||||
|  |     fn while_statement_with_call_condition() { | ||||||
|  |         parses_to(Rule::WhileStatement, "while (foo()) { bar(); }") | ||||||
|  |     } | ||||||
|  |     
 | ||||||
|  |     #[test] | ||||||
|  |     fn for_statement_with_call_iterator() { | ||||||
|  |         parses_to(Rule::ForStatement, indoc! {" | ||||||
|  |             for (foo in bar()) { | ||||||
|  |                 baz(foo); | ||||||
|  |             } | ||||||
|  |         "})
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user