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,81 +28,156 @@ 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 {
|
#[test]
|
||||||
use crate::parser::deimos_parser_tests::parse;
|
fn simple_interface() {
|
||||||
use crate::parser::Rule;
|
parses_to(Rule::CompilationUnit, "pub int Simple { fn foo() -> Void }");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_interface() {
|
fn interface_with_op() {
|
||||||
parse(Rule::CompilationUnit, "pub int Simple { fn foo() -> Void }");
|
parses_to(
|
||||||
}
|
Rule::CompilationUnit,
|
||||||
|
"pub int Callable { op () () -> Void }",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interface_with_op() {
|
fn interface_with_alias() {
|
||||||
parse(
|
parses_to(
|
||||||
Rule::CompilationUnit,
|
Rule::CompilationUnit,
|
||||||
"pub int Callable { op () () -> Void }",
|
indoc! {"
|
||||||
);
|
pub int Callable {
|
||||||
}
|
fn call() -> Void
|
||||||
|
def op () () -> Void alias call
|
||||||
|
}
|
||||||
|
"},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn interface_with_alias() {
|
fn index_identifier() {
|
||||||
parse(
|
parses_to(Rule::SuffixExpression, "foo[0]");
|
||||||
Rule::CompilationUnit,
|
}
|
||||||
"pub int Callable {\n fn call() -> Void\n op () () -> Void alias call\n}",
|
|
||||||
);
|
#[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