1
1
mirror of https://github.com/tweag/nickel.git synced 2024-10-06 16:18:08 +03:00

Make declarations point to value explicitly

This commit is contained in:
Yannik Sander 2022-01-27 21:59:17 +01:00
parent 9acf0410de
commit 31e38d587e
7 changed files with 106 additions and 35 deletions

View File

@ -5,7 +5,7 @@ use nickel::{
identifier::Ident,
term::{MetaValue, RichTerm, Term},
typecheck::{
linearization::{LinearizationState, Scope, ScopeId},
linearization::{LinearizationState, Scope},
TypeWrapper,
},
types::AbsType,
@ -13,7 +13,10 @@ use nickel::{
use crate::linearization::interface::{TermKind, UsageState};
use super::{interface::Unresolved, Environment, IdGen, LinearizationItem};
use super::{
interface::{Unresolved, ValueState},
Environment, IdGen, LinearizationItem,
};
/// A concrete [LinearizationState]
/// Holds any inner datatype that can be used as stable resource
@ -52,11 +55,21 @@ impl Building {
TermKind::Structure => unreachable!(),
TermKind::Usage(_) => unreachable!(),
TermKind::Record(_) => unreachable!(),
TermKind::Declaration(_, ref mut usages)
TermKind::Declaration(_, ref mut usages, _)
| TermKind::RecordField { ref mut usages, .. } => usages.push(usage),
};
}
pub(super) fn inform_declaration(&mut self, declaration: ID, value: ID) {
match self.linearization.get_mut(declaration) {
Some(LinearizationItem {
kind: TermKind::Declaration(_, _, value_state),
..
}) => *value_state = ValueState::Known(value),
_ => (),
}
}
pub(super) fn register_fields(
&mut self,
record_fields: &HashMap<Ident, RichTerm>,
@ -75,7 +88,7 @@ impl Building {
record,
ident: ident.clone(),
usages: Vec::new(),
value: None,
value: ValueState::Unknown,
},
scope: scope.clone(),
meta: match value.term.as_ref() {
@ -120,16 +133,19 @@ impl Building {
}
// load referenced value, either from record field or declaration
.and_then(|item_pointer| {
match item_pointer.kind {
match &item_pointer.kind {
// if declaration is a record field, resolve its value
TermKind::RecordField { value, .. } => {
debug!("parent referenced a record field {:?}", value);
value
// retrieve record
.as_option()
.and_then(|value_index| self.linearization.get(value_index))
}
// if declaration is a let biding resolve its value
TermKind::Declaration(_, _) => self.linearization.get(item_pointer.id + 1),
// if declaration is a let binding resolve its value
TermKind::Declaration(_, _, ValueState::Known(value)) => {
self.linearization.get(*value)
}
// if something else was referenced, stop.
_ => Some(item_pointer),
@ -189,12 +205,13 @@ impl Building {
});
let referenced_declaration =
referenced_declaration.and_then(|referenced| match referenced.kind {
referenced_declaration.and_then(|referenced| match &referenced.kind {
TermKind::Usage(UsageState::Resolved(pointed)) => {
self.linearization.get(pointed)
self.linearization.get(*pointed)
}
TermKind::RecordField { value, .. } => value
// retrieve record
.as_option()
.and_then(|value_index| self.linearization.get(value_index))
// retrieve field
.and_then(|record| match &record.kind {

View File

@ -8,7 +8,7 @@ use nickel::{
use super::{
building::ID,
interface::{Resolved, TermKind, UsageState},
interface::{Resolved, TermKind, UsageState, ValueState},
LinearizationItem,
};
@ -108,8 +108,20 @@ impl Completed {
let mut extra = Vec::new();
let item = match item.kind {
TermKind::Usage(UsageState::Resolved(usage)) => self.get_item(usage).unwrap_or(item),
TermKind::Declaration(_, _) => self.get_item(item.id).unwrap_or(item),
TermKind::Usage(UsageState::Resolved(declaration)) => self
.get_item(declaration)
.and_then(|decl| match decl.kind {
TermKind::Declaration(_, _, ValueState::Known(value))
| TermKind::RecordField {
value: ValueState::Known(value),
..
} => self.get_item(value),
_ => None,
})
.unwrap_or(item),
TermKind::Declaration(_, _, ValueState::Known(value)) => {
self.get_item(value).unwrap_or(item)
}
_ => item,
};

View File

@ -24,18 +24,32 @@ impl ResolutionState for Resolved {}
/// Can be extended later to represent Contracts, Records, etc.
#[derive(Debug, Clone, PartialEq)]
pub enum TermKind {
Declaration(Ident, Vec<ID>),
Declaration(Ident, Vec<ID>, ValueState),
Usage(UsageState),
Record(HashMap<Ident, ID>),
RecordField {
ident: Ident,
record: ID,
usages: Vec<ID>,
value: Option<ID>,
value: ValueState,
},
Structure,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValueState {
Unknown,
Known(ID),
}
impl ValueState {
pub fn as_option(&self) -> Option<ID> {
match self {
ValueState::Unknown => None,
ValueState::Known(value) => Some(*value),
}
}
}
/// Some usages cannot be fully resolved in a first pass (i.e. recursive record fields)
/// In these cases we defer the resolution to a second pass during linearization
#[derive(Debug, Clone, PartialEq)]

View File

@ -3,7 +3,6 @@ use std::collections::HashMap;
use codespan::ByteIndex;
use log::debug;
use nickel::{
destruct,
identifier::Ident,
position::{RawSpan, TermPos},
term::{MetaValue, RichTerm, Term, UnaryOp},
@ -15,8 +14,10 @@ use nickel::{
types::AbsType,
};
use crate::linearization::interface::ValueState;
use self::{
building::Building,
building::{Building, ID},
completed::Completed,
interface::{ResolutionState, TermKind, UsageState},
};
@ -57,7 +58,8 @@ pub struct AnalysisHost {
/// in their own scope immediately after the record, which
/// gives the corresponding record field _term_ to the ident
/// useable to construct a vale declaration.
record_fields: Option<(usize, Vec<(usize, Ident)>)>,
record_fields: Option<(ID, Vec<(ID, Ident)>)>,
let_binding: Option<ID>,
/// Accesses to nested records are recorded recursively.
/// ```
/// outer.middle.inner -> inner(middle(outer))
@ -70,14 +72,7 @@ pub struct AnalysisHost {
impl AnalysisHost {
pub fn new() -> Self {
AnalysisHost {
env: Environment::new(),
scope: Default::default(),
next_scope_id: Default::default(),
meta: None,
record_fields: None,
access: None,
}
Default::default()
}
}
@ -89,6 +84,7 @@ impl Default for AnalysisHost {
next_scope_id: Default::default(),
meta: Default::default(),
record_fields: Default::default(),
let_binding: Default::default(),
access: Default::default(),
}
}
@ -113,7 +109,10 @@ impl Linearizer for AnalysisHost {
// `record` is the id [LinearizatonItem] of the enclosing record
// `offset` is used to find the [LinearizationItem] representing the field
// Field items are inserted immediately after the record
if !matches!(term, Term::Op1(UnaryOp::StaticAccess(_), _)) {
if !matches!(
term,
Term::Op1(UnaryOp::StaticAccess(_), _) | Term::MetaValue(_)
) {
if let Some((record, (offset, Ident { pos: field_pos, .. }))) = self
.record_fields
.take()
@ -139,7 +138,7 @@ impl Linearizer for AnalysisHost {
};
match field.kind {
TermKind::RecordField { ref mut value, .. } => {
*value = Some(id_gen.get() + usage_offset);
*value = ValueState::Known(id_gen.get() + usage_offset);
}
// The linearization item of a record with n fields is expected to be
// followed by n linearization items representing each field
@ -147,6 +146,10 @@ impl Linearizer for AnalysisHost {
}
}
}
if let Some(declaration) = self.let_binding.take() {
lin.inform_declaration(declaration, id_gen.get());
}
}
if pos == TermPos::None {
@ -158,25 +161,41 @@ impl Linearizer for AnalysisHost {
Term::LetPattern(ident, destruct, ..) | Term::FunPattern(ident, destruct, _) => {
if let Some(ident) = ident {
self.env.insert(ident.to_owned(), id);
let id = id_gen.get_and_advance();
let value_ptr = match term {
Term::LetPattern(_, _, _, _) => {
self.let_binding = Some(id);
ValueState::Unknown
}
Term::FunPattern(_, _, _) => ValueState::Known(id),
_ => unreachable!(),
};
lin.push(LinearizationItem {
id: id_gen.get_and_advance(),
id,
ty,
pos: ident.pos.unwrap(),
scope: self.scope.clone(),
kind: TermKind::Declaration(ident.to_owned(), Vec::new()),
kind: TermKind::Declaration(ident.to_owned(), Vec::new(), value_ptr),
meta: self.meta.take(),
});
}
for matched in destruct.to_owned().inner() {
let (ident, term) = matched.as_meta_field();
self.env.insert(ident.to_owned(), id_gen.get());
let id = id_gen.get_and_advance();
lin.push(LinearizationItem {
id: id_gen.get_and_advance(),
id,
// TODO: get type from pattern
ty: TypeWrapper::Concrete(AbsType::Dyn()),
pos: ident.pos.unwrap(),
scope: self.scope.clone(),
kind: TermKind::Declaration(ident.to_owned(), Vec::new()),
kind: TermKind::Declaration(
ident.to_owned(),
Vec::new(),
ValueState::Known(id),
),
meta: match &*term.term {
Term::MetaValue(meta) => Some(MetaValue {
value: None,
@ -189,12 +208,20 @@ impl Linearizer for AnalysisHost {
}
Term::Let(ident, _, _, _) | Term::Fun(ident, _) => {
self.env.insert(ident.to_owned(), id);
let value_ptr = match term {
Term::LetPattern(_, _, _, _) => {
self.let_binding = Some(id);
ValueState::Unknown
}
Term::FunPattern(_, _, _) => ValueState::Known(id),
_ => unreachable!(),
};
lin.push(LinearizationItem {
id,
ty,
pos: ident.pos.unwrap(),
scope: self.scope.clone(),
kind: TermKind::Declaration(ident.to_owned(), Vec::new()),
kind: TermKind::Declaration(ident.to_owned(), Vec::new(), value_ptr),
meta: self.meta.take(),
});
}
@ -440,6 +467,7 @@ impl Linearizer for AnalysisHost {
record_fields: self.record_fields.as_mut().and_then(|(record, fields)| {
Some(*record).zip(fields.pop().map(|field| vec![field]))
}),
let_binding: None,
access: self.access.clone(),
}
}

View File

@ -46,7 +46,7 @@ pub fn handle_completion(
.get_in_scope(&item)
.iter()
.filter_map(|i| match i.kind {
TermKind::Declaration(ref ident, _) => Some((ident.clone(), i.ty.clone())),
TermKind::Declaration(ref ident, _, _) => Some((ident.clone(), i.ty.clone())),
_ => None,
})
.map(|(ident, _)| CompletionItem {

View File

@ -118,7 +118,7 @@ pub fn handle_to_usages(
debug!("found referencing item: {:?}", item);
let locations = match &item.kind {
TermKind::Declaration(_, usages) | TermKind::RecordField { usages, .. } => {
TermKind::Declaration(_, usages, _) | TermKind::RecordField { usages, .. } => {
let mut locations = Vec::new();
for reference_id in usages.iter() {

View File

@ -25,7 +25,7 @@ pub fn handle_document_symbols(
.linearization
.iter()
.filter_map(|item| match &item.kind {
TermKind::Declaration(name, _) => {
TermKind::Declaration(name, _, _) => {
let (file_id, span) = item.pos.to_range();
let range =