From 431b3255ef6af51648f1d5091024b5887765f89e Mon Sep 17 00:00:00 2001 From: oxalica Date: Sun, 24 Jul 2022 06:28:54 +0800 Subject: [PATCH] Impl all literals lowering --- Cargo.lock | 19 ++++++++ Cargo.toml | 1 + src/def/lower.rs | 59 ++++++++++++++++++++--- src/def/mod.rs | 3 ++ src/def/tests/lower.rs | 77 ++++++++++++++++++++++++++++++ src/def/{tests.rs => tests/mod.rs} | 28 ++--------- 6 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 src/def/tests/lower.rs rename src/def/{tests.rs => tests/mod.rs} (54%) diff --git a/Cargo.lock b/Cargo.lock index f0516e1..38ccfee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,12 +137,22 @@ version = "0.0.0" dependencies = [ "expect-test", "la-arena", + "ordered-float", "rowan", "salsa", "smol_str", "syntax", ] +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.13.0" @@ -155,6 +165,15 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "ordered-float" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bcbab4bfea7a59c2c0fe47211a1ac4e3e96bea6eb446d704f310bc5c732ae2" +dependencies = [ + "num-traits", +] + [[package]] name = "parking_lot" version = "0.11.2" diff --git a/Cargo.toml b/Cargo.toml index 0f4e2ea..842c5fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] la-arena = "0.2.1" +ordered-float = "3.0.0" rowan = "0.15.6" salsa = "0.17.0-pre.2" smol_str = "0.1.23" diff --git a/src/def/lower.rs b/src/def/lower.rs index ced4370..6125f51 100644 --- a/src/def/lower.rs +++ b/src/def/lower.rs @@ -1,4 +1,4 @@ -use super::{AstPtr, Expr, ExprId, Literal, Module, ModuleSourceMap}; +use super::{AstPtr, Expr, ExprId, Literal, Module, ModuleSourceMap, Path, PathAnchor}; use crate::source::{FileId, InFile}; use rowan::ast::AstNode; use syntax::ast::{self, LiteralKind}; @@ -71,16 +71,61 @@ impl LowerCtx { fn lower_literal(&mut self, lit: ast::Literal) -> Option { let kind = lit.kind()?; let tok = lit.token().unwrap(); - let text = tok.text(); + let mut text = tok.text(); + + fn normalize_path(path: &str) -> (usize, Box) { + let mut ret = String::new(); + let mut supers = 0usize; + for seg in path.split('/').filter(|&seg| !seg.is_empty() && seg != ".") { + if seg != ".." { + if !ret.is_empty() { + ret.push('/'); + } + ret.push_str(seg); + } else if ret.is_empty() { + supers += 1; + } else { + let last_slash = ret.bytes().rposition(|c| c != b'/').unwrap_or(0); + ret.truncate(last_slash); + } + } + (supers, ret.into()) + } Some(match kind { LiteralKind::Int => Literal::Int(text.parse::().ok()?), - LiteralKind::Float => todo!(), + LiteralKind::Float => Literal::Float(text.parse::().unwrap().into()), LiteralKind::Uri => Literal::String(text.into()), - LiteralKind::RelativePath => todo!(), - LiteralKind::AbsolutePath => todo!(), - LiteralKind::HomePath => todo!(), - LiteralKind::SearchPath => todo!(), + LiteralKind::RelativePath + | LiteralKind::AbsolutePath + | LiteralKind::HomePath + | LiteralKind::SearchPath => { + let anchor = match kind { + LiteralKind::RelativePath => PathAnchor::Relative(self.file_id), + LiteralKind::AbsolutePath => PathAnchor::Absolute, + LiteralKind::HomePath => { + text = &text[2..]; // Strip "~/". + PathAnchor::Home + } + LiteralKind::SearchPath => { + text = &text[1..text.len() - 1]; // Strip '<' and '>'. + let (search_name, path) = text.split_once('/').unwrap(); + text = path; + PathAnchor::Search(search_name.into()) + } + _ => unreachable!(), + }; + let (mut supers, raw_segments) = normalize_path(text); + if kind == LiteralKind::AbsolutePath { + // Extra ".." has no effect for absolute path. + supers = 0; + } + Literal::Path(Path { + anchor, + supers, + raw_segments, + }) + } }) } } diff --git a/src/def/mod.rs b/src/def/mod.rs index ce7db15..1429317 100644 --- a/src/def/mod.rs +++ b/src/def/mod.rs @@ -5,6 +5,7 @@ mod tests; use crate::source::{FileId, SourceDatabase}; use la_arena::{Arena, Idx}; +use ordered_float::OrderedFloat; use smol_str::SmolStr; use std::collections::HashMap; use std::ops; @@ -70,6 +71,7 @@ pub enum Expr { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Literal { Int(i64), + Float(OrderedFloat), String(Box), Path(Path), // TODO @@ -93,6 +95,7 @@ impl From for Name { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Path { pub anchor: PathAnchor, + pub supers: usize, // Normalized path separated by `/`, with no `.` or `..` segments. pub raw_segments: Box, } diff --git a/src/def/tests/lower.rs b/src/def/tests/lower.rs new file mode 100644 index 0000000..c9bbe22 --- /dev/null +++ b/src/def/tests/lower.rs @@ -0,0 +1,77 @@ +use crate::def::lower::lower; +use crate::source::{FileId, InFile}; +use expect_test::{expect, Expect}; +use std::fmt::Write; +use syntax::parse_file; + +fn check_lower(src: &str, expect: Expect) { + let parse = parse_file(src); + let (module, _source_map) = lower(InFile::new(FileId(0), parse.root())); + let mut got = String::new(); + for (i, e) in module.exprs.iter() { + writeln!(got, "{}: {:?}", i.into_raw(), e).unwrap(); + } + expect.assert_eq(&got); +} + +#[test] +fn literal() { + check_lower( + "42", + expect![[r#" + 0: Literal(Int(42)) + "#]], + ); + check_lower( + "1.2e3", + expect![[r#" + 0: Literal(Float(OrderedFloat(1200.0))) + "#]], + ); + check_lower( + "a:b", + expect![[r#" + 0: Literal(String("a:b")) + "#]], + ); +} + +#[test] +fn path() { + check_lower( + "./.", + expect![[r#" + 0: Literal(Path(Path { anchor: Relative(FileId(0)), supers: 0, raw_segments: "" })) + "#]], + ); + check_lower( + "../.", + expect![[r#" + 0: Literal(Path(Path { anchor: Relative(FileId(0)), supers: 1, raw_segments: "" })) + "#]], + ); + check_lower( + "../a/../../.b/./c", + expect![[r#" + 0: Literal(Path(Path { anchor: Relative(FileId(0)), supers: 2, raw_segments: ".b/c" })) + "#]], + ); + check_lower( + "/../a/../../.b/./c", + expect![[r#" + 0: Literal(Path(Path { anchor: Absolute, supers: 0, raw_segments: ".b/c" })) + "#]], + ); + check_lower( + "~/../a/../../.b/./c", + expect![[r#" + 0: Literal(Path(Path { anchor: Home, supers: 2, raw_segments: ".b/c" })) + "#]], + ); + check_lower( + "

", + expect![[r#" + 0: Literal(Path(Path { anchor: Search("p"), supers: 2, raw_segments: ".b/c" })) + "#]], + ); +} diff --git a/src/def/tests.rs b/src/def/tests/mod.rs similarity index 54% rename from src/def/tests.rs rename to src/def/tests/mod.rs index dee3a5d..f4f60a2 100644 --- a/src/def/tests.rs +++ b/src/def/tests/mod.rs @@ -2,33 +2,11 @@ use super::DefDatabase; use crate::tests::TestDB; use expect_test::expect; +mod lower; + #[test] -fn module_basic() { +fn source_map() { let (db, root_id) = TestDB::from_file("foo 123"); - expect![[r#" - Module { - exprs: Arena { - len: 3, - data: [ - Ident( - Name( - "foo", - ), - ), - Literal( - Int( - 123, - ), - ), - Apply( - Idx::(0), - Idx::(1), - ), - ], - }, - } - "#]] - .assert_debug_eq(&db.module(root_id)); let source_map = db.source_map(root_id); let mut expr_map = source_map.expr_map.iter().collect::>();