1
1
mirror of https://github.com/oxalica/nil.git synced 2024-10-27 20:44:38 +03:00

Impl all literals lowering

This commit is contained in:
oxalica 2022-07-24 06:28:54 +08:00
parent d0132c886c
commit 431b3255ef
6 changed files with 155 additions and 32 deletions

19
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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<Literal> {
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<str>) {
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::<i64>().ok()?),
LiteralKind::Float => todo!(),
LiteralKind::Float => Literal::Float(text.parse::<f64>().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,
})
}
})
}
}

View File

@ -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<f64>),
String(Box<str>),
Path(Path),
// TODO
@ -93,6 +95,7 @@ impl From<String> 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<str>,
}

77
src/def/tests/lower.rs Normal file
View File

@ -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(
"<p/../a/../../.b/./c>",
expect![[r#"
0: Literal(Path(Path { anchor: Search("p"), supers: 2, raw_segments: ".b/c" }))
"#]],
);
}

View File

@ -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::<Expr>(0),
Idx::<Expr>(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::<Vec<_>>();