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:
parent
9acf0410de
commit
31e38d587e
@ -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 {
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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 =
|
||||
|
Loading…
Reference in New Issue
Block a user