feat: add ASTDiff and impl lazy compilation

This commit is contained in:
Shunsuke Shibayama 2023-05-13 11:33:48 +09:00
parent cac2c51cd4
commit 33e1b776cb
9 changed files with 140 additions and 7 deletions

View file

@ -77,9 +77,9 @@ impl<Checker: BuildRunnable> Server<Checker> {
return Ok(());
};
let mut parser = Parser::new(ts);
if parser.parse().is_err() {
let Ok(module) = parser.parse() else {
return Ok(());
}
};
let path = util::uri_to_path(&uri);
let code = self.file_cache.get_entire_code(&uri)?;
let mode = if path.to_string_lossy().ends_with(".d.er") {
@ -87,6 +87,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
} else {
"exec"
};
if let Some(mut lowerer) = self.get_lowerer(&path) {}
let mut checker = self.get_checker(path);
match checker.build(code, mode) {
Ok(artifact) => {

93
crates/els/diff.rs Normal file
View file

@ -0,0 +1,93 @@
use std::cmp::Ordering::*;
use erg_common::traits::Stream;
use erg_compiler::erg_parser::ast;
use erg_compiler::erg_parser::ast::AST;
use erg_compiler::hir;
use erg_compiler::hir::HIR;
use erg_compiler::lower::ASTLowerer;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ASTDiff {
Deletion(usize),
Addition(usize, ast::Expr),
Modification(usize, ast::Expr),
Nop,
}
/// diff(old: {x, y, z}, new: {x, a, y, z}) => ASTDiff::Addition(1)
/// diff(old: {x, y}, new: {x, y, a}) => ASTDiff::Addition(2)
/// diff(old: {x, y}, new: {x}) => ASTDiff::Deletion(1)
/// diff(old: {x, y, z}, new: {x, z}) => ASTDiff::Deletion(1)
/// diff(old: {x, y, z}, new: {x, ya, z}) => ASTDiff::Modification(1)
/// diff(old: {x, y, z}, new: {x, a, z}) => ASTDiff::Modification(1)
/// diff(old: {x, y, z}, new: {x, y, z}) => ASTDiff::Nop
impl ASTDiff {
pub fn diff(old: AST, new: AST) -> ASTDiff {
match old.module.len().cmp(&new.module.len()) {
Less => {
let idx = new
.module
.iter()
.zip(old.module.iter())
.position(|(new, old)| new != old)
.unwrap();
Self::Addition(idx, new.module.get(idx).unwrap().clone())
}
Greater => Self::Deletion(
old.module
.iter()
.zip(new.module.iter())
.position(|(old, new)| old != new)
.unwrap(),
),
Equal => old
.module
.iter()
.zip(new.module.iter())
.position(|(old, new)| old != new)
.map(|idx| Self::Modification(idx, new.module.get(idx).unwrap().clone()))
.unwrap_or(Self::Nop),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HIRDiff {
Deletion(usize),
Addition(usize, hir::Expr),
Modification(usize, hir::Expr),
Nop,
}
impl HIRDiff {
pub fn new(diff: ASTDiff, lowerer: &mut ASTLowerer) -> Option<Self> {
match diff {
ASTDiff::Deletion(idx) => Some(Self::Deletion(idx)),
ASTDiff::Addition(idx, expr) => {
let expr = lowerer.lower_expr(expr).ok()?;
Some(Self::Addition(idx, expr))
}
ASTDiff::Modification(idx, expr) => {
let expr = lowerer.lower_expr(expr).ok()?;
Some(Self::Modification(idx, expr))
}
ASTDiff::Nop => Some(Self::Nop),
}
}
pub fn update(self, old: &mut HIR) {
match self {
Self::Addition(idx, expr) => {
old.module.insert(idx, expr);
}
Self::Deletion(usize) => {
old.module.remove(usize);
}
Self::Modification(idx, expr) => {
*old.module.get_mut(idx).unwrap() = expr;
}
Self::Nop => {}
}
}
}

View file

@ -38,9 +38,9 @@ pub struct FileCacheEntry {
impl FileCacheEntry {
/// line: 0-based
pub fn get_line(&self, line: u32) -> Option<String> {
pub fn get_line(&self, line0: u32) -> Option<String> {
let mut lines = self.code.lines();
lines.nth(line as usize).map(|s| s.to_string())
lines.nth(line0 as usize).map(|s| s.to_string())
}
}
@ -164,9 +164,9 @@ impl FileCache {
}
/// 0-based
pub(crate) fn get_line(&self, uri: &NormalizedUrl, line: u32) -> Option<String> {
pub(crate) fn get_line(&self, uri: &NormalizedUrl, line0: u32) -> Option<String> {
let _ = self.load_once(uri);
self.files.borrow_mut().get(uri)?.get_line(line)
self.files.borrow_mut().get(uri)?.get_line(line0)
}
pub(crate) fn get_ranged(

View file

@ -4,6 +4,7 @@ mod command;
mod completion;
mod definition;
mod diagnostics;
mod diff;
mod file_cache;
mod hir_visitor;
mod hover;

View file

@ -4,6 +4,7 @@ mod command;
mod completion;
mod definition;
mod diagnostics;
mod diff;
mod file_cache;
mod hir_visitor;
mod hover;

View file

@ -3,9 +3,12 @@ use std::io;
use std::io::{stdin, stdout, BufRead, Read, StdinLock, StdoutLock, Write};
use std::ops::Not;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::str::FromStr;
use erg_common::consts::PYTHON_MODE;
use erg_compiler::error::{CompileErrors, CompileWarnings};
use erg_compiler::lower::ASTLowerer;
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_json::Value;
@ -538,6 +541,21 @@ impl<Checker: BuildRunnable> Server<Checker> {
}
}
pub(crate) fn get_lowerer(&self, path: &Path) -> Option<ASTLowerer> {
let module = Rc::get_mut(&mut self.get_shared().unwrap().mod_cache.get_mut(path)?.module)?;
let module = std::mem::take(module);
Some(ASTLowerer::new_with_ctx(module))
}
pub(crate) fn restore_mod_ctx(&self, path: &Path, module: ModuleContext) {
self.get_shared()
.unwrap()
.mod_cache
.get_mut(path)
.unwrap()
.module = Rc::new(module);
}
pub(crate) fn get_visitor(&self, uri: &NormalizedUrl) -> Option<HIRVisitor> {
self.artifacts
.get(uri)?

View file

@ -351,6 +351,12 @@ macro_rules! impl_stream {
}
}
impl std::ops::IndexMut<usize> for $Strc {
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
erg_common::traits::Stream::get_mut(self, idx).unwrap()
}
}
impl From<$Strc> for Vec<$Inner> {
fn from(item: $Strc) -> Vec<$Inner> {
item.payload()

View file

@ -1179,4 +1179,8 @@ impl ModuleContext {
scope,
}
}
pub fn get_top_cfg(&self) -> ErgConfig {
self.context.cfg.clone()
}
}

View file

@ -175,6 +175,15 @@ impl ASTLowerer {
}
}
pub fn new_with_ctx(module: ModuleContext) -> Self {
Self {
cfg: module.get_top_cfg(),
module,
errs: LowerErrors::empty(),
warns: LowerWarnings::empty(),
}
}
fn pop_append_errs(&mut self) {
match self.module.context.check_decls_and_pop() {
Ok(ctx) if self.cfg.mode == ErgMode::LanguageServer && !ctx.dir().is_empty() => {
@ -2307,7 +2316,7 @@ impl ASTLowerer {
// Call.obj == Accessor cannot be type inferred by itself (it can only be inferred with arguments)
// so turn off type checking (check=false)
fn lower_expr(&mut self, expr: ast::Expr) -> LowerResult<hir::Expr> {
pub fn lower_expr(&mut self, expr: ast::Expr) -> LowerResult<hir::Expr> {
log!(info "entered {}", fn_name!());
match expr {
ast::Expr::Literal(lit) => Ok(hir::Expr::Lit(self.lower_literal(lit)?)),