Compare commits
3 Commits
9805a3aad5
...
6ab9efa8fd
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6ab9efa8fd | ||
![]() |
2b4e042602 | ||
![]() |
ce20cece21 |
56
Cargo.lock
generated
56
Cargo.lock
generated
@ -106,6 +106,17 @@ version = "0.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codespan-reporting"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"termcolor",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@ -136,6 +147,7 @@ name = "deimos"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"codespan-reporting",
|
||||||
"pest",
|
"pest",
|
||||||
"pest_derive",
|
"pest_derive",
|
||||||
]
|
]
|
||||||
@ -253,6 +265,26 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.8"
|
version = "0.10.8"
|
||||||
@ -281,6 +313,15 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
@ -319,6 +360,12 @@ version = "1.0.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -331,6 +378,15 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
|
@ -15,3 +15,4 @@ path = "src/bin/dmc/main.rs"
|
|||||||
pest = "2.7.14"
|
pest = "2.7.14"
|
||||||
clap = { version = "4.5.23", features = ["derive"] }
|
clap = { version = "4.5.23", features = ["derive"] }
|
||||||
pest_derive = "2.7.14"
|
pest_derive = "2.7.14"
|
||||||
|
codespan-reporting = "0.12.0"
|
||||||
|
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
13
sketching/may_2025/name_one.dm
Normal file
13
sketching/may_2025/name_one.dm
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
ns greeter
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = 'Hello';
|
||||||
|
let y = 'World';
|
||||||
|
{
|
||||||
|
let x = 'Hello';
|
||||||
|
let test = 'Test';
|
||||||
|
let test = 'Again';
|
||||||
|
};
|
||||||
|
x = y;
|
||||||
|
let x = 'Hello!';
|
||||||
|
}
|
@ -14,19 +14,29 @@ pub fn build_ast(compilation_unit_pair: Pair<Rule>) -> CompilationUnit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_identifier(identifier_pair: Pair<Rule>) -> Identifier {
|
fn build_identifier(identifier_pair: Pair<Rule>) -> Identifier {
|
||||||
Identifier {
|
let as_span = identifier_pair.as_span();
|
||||||
name: identifier_pair.as_str().to_string(),
|
Identifier::new(
|
||||||
}
|
as_span.as_str(),
|
||||||
|
Range {
|
||||||
|
start: as_span.start(),
|
||||||
|
end: as_span.end(),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_fqn(fqn_pair: Pair<Rule>) -> FullyQualifiedName {
|
fn build_fqn(fqn_pair: Pair<Rule>) -> FullyQualifiedName {
|
||||||
FullyQualifiedName(
|
let as_span = fqn_pair.as_span();
|
||||||
|
FullyQualifiedName::new(
|
||||||
fqn_pair
|
fqn_pair
|
||||||
.into_inner()
|
.into_inner()
|
||||||
.map(|identifier_pair| {
|
.map(|identifier_pair| {
|
||||||
expect_and_use(identifier_pair, Rule::Identifier, build_identifier)
|
expect_and_use(identifier_pair, Rule::Identifier, build_identifier)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
Range {
|
||||||
|
start: as_span.start(),
|
||||||
|
end: as_span.end(),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,14 +755,10 @@ fn build_call_statement(call_statement_pair: Pair<Rule>) -> CallStatement {
|
|||||||
while let Some(inner_pair) = inner.next() {
|
while let Some(inner_pair) = inner.next() {
|
||||||
match inner_pair.as_rule() {
|
match inner_pair.as_rule() {
|
||||||
Rule::ObjectAccess => {
|
Rule::ObjectAccess => {
|
||||||
result = Expression::ObjectAccess(build_object_access(
|
result = Expression::ObjectAccess(build_object_access(result, inner_pair));
|
||||||
result, inner_pair,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Rule::ParenthesesCall => {
|
Rule::ParenthesesCall => {
|
||||||
result = Expression::Call(build_call_expression(
|
result = Expression::Call(build_call_expression(result, inner_pair));
|
||||||
result, inner_pair,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Rule::PlusPlus => {
|
Rule::PlusPlus => {
|
||||||
result = Expression::UnarySuffix(SuffixExpression {
|
result = Expression::UnarySuffix(SuffixExpression {
|
||||||
@ -971,16 +977,10 @@ fn build_suffix_expression(suffix_pair: Pair<Rule>) -> Expression {
|
|||||||
while let Some(suffix_pair) = inner.next() {
|
while let Some(suffix_pair) = inner.next() {
|
||||||
match suffix_pair.as_rule() {
|
match suffix_pair.as_rule() {
|
||||||
Rule::ObjectAccess => {
|
Rule::ObjectAccess => {
|
||||||
result = Expression::ObjectAccess(build_object_access(
|
result = Expression::ObjectAccess(build_object_access(result, suffix_pair))
|
||||||
result,
|
|
||||||
suffix_pair,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Rule::ParenthesesCall => {
|
Rule::ParenthesesCall => {
|
||||||
result = Expression::Call(build_call_expression(
|
result = Expression::Call(build_call_expression(result, suffix_pair))
|
||||||
result,
|
|
||||||
suffix_pair,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
Rule::PlusPlus => {
|
Rule::PlusPlus => {
|
||||||
result = Expression::UnarySuffix(SuffixExpression {
|
result = Expression::UnarySuffix(SuffixExpression {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
use crate::compile::name_analysis::Symbol;
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
|
use std::range::Range;
|
||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
pub mod named;
|
||||||
pub mod pretty_print;
|
pub mod pretty_print;
|
||||||
pub mod unparse;
|
pub mod unparse;
|
||||||
// Operators
|
// Operators
|
||||||
@ -52,10 +55,72 @@ pub enum SuffixUnaryOperator {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Identifier {
|
pub struct Identifier {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub range: Range<usize>,
|
||||||
|
scope_id: Option<usize>,
|
||||||
|
symbol: Option<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Identifier {
|
||||||
|
pub fn new(name: &str, range: Range<usize>) -> Self {
|
||||||
|
Identifier {
|
||||||
|
name: name.to_string(),
|
||||||
|
range,
|
||||||
|
scope_id: None,
|
||||||
|
symbol: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_scope_id(&mut self, id: usize) {
|
||||||
|
self.scope_id = Some(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> Option<usize> {
|
||||||
|
self.scope_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_symbol(&mut self, symbol: Symbol) {
|
||||||
|
self.symbol = Some(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symbol(&self) -> &Option<Symbol> {
|
||||||
|
&self.symbol
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FullyQualifiedName(pub Vec<Identifier>);
|
pub struct FullyQualifiedName {
|
||||||
|
pub identifiers: Vec<Identifier>,
|
||||||
|
pub range: Range<usize>,
|
||||||
|
scope_id: Option<usize>,
|
||||||
|
symbol: Option<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FullyQualifiedName {
|
||||||
|
pub fn new(identifiers: Vec<Identifier>, range: Range<usize>) -> Self {
|
||||||
|
FullyQualifiedName {
|
||||||
|
identifiers,
|
||||||
|
range,
|
||||||
|
scope_id: None,
|
||||||
|
symbol: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_scope_id(&mut self, scope_id: usize) {
|
||||||
|
self.scope_id = Some(scope_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_id(&self) -> Option<usize> {
|
||||||
|
self.scope_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_symbol(&mut self, symbol: Symbol) {
|
||||||
|
self.symbol = Some(symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn symbol(&self) -> &Option<Symbol> {
|
||||||
|
&self.symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Type Use */
|
/* Type Use */
|
||||||
|
|
||||||
@ -400,10 +465,10 @@ pub enum Statement {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VariableDeclarationStatement {
|
pub struct VariableDeclarationStatement {
|
||||||
is_mutable: bool,
|
pub is_mutable: bool,
|
||||||
identifier: Identifier,
|
pub identifier: Identifier,
|
||||||
declared_type: Option<TypeUse>,
|
pub declared_type: Option<TypeUse>,
|
||||||
initializer: Option<Expression>,
|
pub initializer: Option<Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
29
src/ast/named.rs
Normal file
29
src/ast/named.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use crate::ast::{FullyQualifiedName, Identifier};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
pub trait Named {
|
||||||
|
fn name(&self) -> Cow<'_, str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for Identifier {
|
||||||
|
fn name(&self) -> Cow<'_, str> {
|
||||||
|
Cow::Borrowed(&self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for FullyQualifiedName {
|
||||||
|
fn name(&self) -> Cow<'_, str> {
|
||||||
|
if self.identifiers.len() == 1 {
|
||||||
|
self.identifiers[0].name()
|
||||||
|
} else {
|
||||||
|
let mut acc = String::new();
|
||||||
|
for (i, identifier) in self.identifiers.iter().enumerate() {
|
||||||
|
acc += &identifier.name();
|
||||||
|
if i < self.identifiers.len() - 1 {
|
||||||
|
acc += "::";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Cow::Owned(acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -74,7 +74,7 @@ impl PrettyPrint for FullyQualifiedName {
|
|||||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
|
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
|
||||||
writer.writeln_indented("FullyQualifiedName")?;
|
writer.writeln_indented("FullyQualifiedName")?;
|
||||||
writer.increase_indent();
|
writer.increase_indent();
|
||||||
for identifier in &self.0 {
|
for identifier in &self.identifiers {
|
||||||
identifier.pretty_print(writer)?;
|
identifier.pretty_print(writer)?;
|
||||||
}
|
}
|
||||||
writer.decrease_indent();
|
writer.decrease_indent();
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
|||||||
|
mod name_analysis;
|
||||||
mod p3;
|
mod p3;
|
||||||
mod unparse;
|
mod unparse;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::name_analysis::name_analysis;
|
||||||
use crate::p3::pretty_print_parse;
|
use crate::p3::pretty_print_parse;
|
||||||
use crate::unparse::unparse;
|
use crate::unparse::unparse;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
@ -24,6 +26,9 @@ enum Commands {
|
|||||||
P3 {
|
P3 {
|
||||||
paths: Vec<PathBuf>,
|
paths: Vec<PathBuf>,
|
||||||
},
|
},
|
||||||
|
NameAnalysis {
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -39,5 +44,13 @@ fn main() {
|
|||||||
pretty_print_parse(&path)
|
pretty_print_parse(&path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Commands::NameAnalysis { paths } => {
|
||||||
|
for path in paths {
|
||||||
|
let result = name_analysis(&path);
|
||||||
|
if let Err(e) = result {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
37
src/bin/dmc/name_analysis.rs
Normal file
37
src/bin/dmc/name_analysis.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use codespan_reporting::files::SimpleFiles;
|
||||||
|
use codespan_reporting::term;
|
||||||
|
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||||
|
use deimos::ast::build::build_ast;
|
||||||
|
use deimos::compile::name_analysis::{analyze_names, SymbolTable};
|
||||||
|
use deimos::parser::{DeimosParser, Rule};
|
||||||
|
use pest::Parser;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn name_analysis(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let src = std::fs::read_to_string(path).unwrap();
|
||||||
|
let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src);
|
||||||
|
let mut files = SimpleFiles::new();
|
||||||
|
let file_id = files.add(path.display().to_string(), &src);
|
||||||
|
|
||||||
|
match parse_result {
|
||||||
|
Ok(mut pairs) => {
|
||||||
|
let compilation_unit_pair = pairs.next().unwrap();
|
||||||
|
let mut compilation_unit = build_ast(compilation_unit_pair);
|
||||||
|
let mut symbol_table = SymbolTable::new();
|
||||||
|
let diagnostics = analyze_names(file_id, &mut compilation_unit, &mut symbol_table);
|
||||||
|
if diagnostics.is_empty() {
|
||||||
|
println!("Name analysis complete.");
|
||||||
|
println!("Symbol table\n-------\n{}", symbol_table);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||||
|
let config = term::Config::default();
|
||||||
|
for diagnostic in diagnostics {
|
||||||
|
term::emit(&mut writer.lock(), &config, &files, &diagnostic)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
use pest::Parser;
|
|
||||||
use deimos::ast::build::build_ast;
|
use deimos::ast::build::build_ast;
|
||||||
use deimos::ast::unparse::Unparse;
|
use deimos::ast::unparse::Unparse;
|
||||||
use deimos::parser::{DeimosParser, Rule};
|
use deimos::parser::{DeimosParser, Rule};
|
||||||
|
use deimos::util::indent_writer::IndentWriter;
|
||||||
|
use pest::Parser;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn unparse(path: &PathBuf) {
|
pub fn unparse(path: &PathBuf) {
|
||||||
let src = std::fs::read_to_string(path).expect(&format!("Could not read {:?}", path));
|
let src = std::fs::read_to_string(path).expect(&format!("Could not read {:?}", path));
|
||||||
@ -11,10 +12,11 @@ pub fn unparse(path: &PathBuf) {
|
|||||||
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(compilation_unit_pair);
|
||||||
let mut out = String::new();
|
let mut writer = IndentWriter::new(0, " ", Box::new(std::io::stdout()));
|
||||||
compilation_unit.unparse(&mut out).expect("Failed to write to string.");
|
compilation_unit
|
||||||
println!("{}", out);
|
.unparse(&mut writer)
|
||||||
},
|
.expect("Failed to write to string.");
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
}
|
}
|
||||||
|
1
src/compile/mod.rs
Normal file
1
src/compile/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod name_analysis;
|
540
src/compile/name_analysis.rs
Normal file
540
src/compile/name_analysis.rs
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
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))
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
|
#![feature(new_range_api)]
|
||||||
#![allow(warnings)]
|
#![allow(warnings)]
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
pub mod compile;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod object_file;
|
pub mod object_file;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
Loading…
Reference in New Issue
Block a user