Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/controller/ast_checker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ impl AstChecker {
},
ast::PureNode::Value(_) => Ok(()),
ast::PureNode::Reference(_) => Ok(()),
// BlockRef arity is enforced by the parser (it refuses to emit
// the variant if args don't match), so it bypasses the
// FuncSignature registry.
ast::PureNode::BlockRef(_) => Ok(()),
}
}
}
Expand Down
64 changes: 62 additions & 2 deletions crates/controller/base/src/traits/block_ref.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{BlockCellId, BlockId, SheetId};
use crate::{BlockCellId, BlockFieldId, BlockId, SheetId};

pub trait BlockRefTrait {
fn get_all_keys(&self, ref_name: &str) -> Vec<(String, SheetId, BlockCellId)>;
Expand All @@ -10,7 +10,11 @@ pub trait BlockRefTrait {
field: &String,
) -> Option<(SheetId, BlockCellId)>;

fn get_all_keys_by_block(&self, sheet_id: SheetId, block_id: BlockId) -> Vec<(String, SheetId, BlockCellId)>;
fn get_all_keys_by_block(
&self,
sheet_id: SheetId,
block_id: BlockId,
) -> Vec<(String, SheetId, BlockCellId)>;
fn get_all_fields_by_block(&self, sheet_id: SheetId, block_id: BlockId) -> Vec<String>;
fn resolve_by_block(
&self,
Expand All @@ -19,4 +23,60 @@ pub trait BlockRefTrait {
key: &String,
field: &String,
) -> Option<(SheetId, BlockCellId)>;

/// Resolve a single block-cell using the stable field id (preferred path
/// after the parse-time id resolution).
fn resolve_by_block_field_id(
&self,
sheet_id: SheetId,
block_id: BlockId,
key: &String,
field_id: BlockFieldId,
) -> Option<(SheetId, BlockCellId)>;

/// Returns `(field_name, field_id)` pairs for a block.
fn get_all_field_ids_by_block(
&self,
sheet_id: SheetId,
block_id: BlockId,
) -> Vec<(String, BlockFieldId)>;

/// Lookup a field id from a field name within a block (used at parse time).
fn resolve_field_id(
&self,
sheet_id: SheetId,
block_id: BlockId,
field: &str,
) -> Option<BlockFieldId>;

/// Inverse of `resolve_field_id` — used for unparse.
fn fetch_field_name(
&self,
sheet_id: SheetId,
block_id: BlockId,
field_id: BlockFieldId,
) -> Option<String>;
}

/// Resolves a `BlockRef` ref-name into a stable `(sheet_id, block_id)` pair, and
/// resolves field names to stable ids. Used by the parser to do the
/// id-substitution at parse time.
pub trait BlockRefResolverTrait {
fn resolve_block_ref_name(&self, ref_name: &str) -> Option<(SheetId, BlockId)>;

fn resolve_block_field(
&self,
sheet_id: SheetId,
block_id: BlockId,
field: &str,
) -> Option<BlockFieldId>;

fn fetch_block_ref_name(&self, sheet_id: SheetId, block_id: BlockId) -> Option<String>;

fn fetch_block_field_name(
&self,
sheet_id: SheetId,
block_id: BlockId,
field_id: BlockFieldId,
) -> Option<String>;
}
22 changes: 20 additions & 2 deletions crates/controller/base/src/traits/name_fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
errors::BasicError, CellId, ColId, Cube, CubeId, ExtBookId, ExtRef, ExtRefId, FuncId, NameId,
Range, RangeId, RowId, SheetId, TextId,
errors::BasicError, BlockFieldId, BlockId, CellId, ColId, Cube, CubeId, ExtBookId, ExtRef,
ExtRefId, FuncId, NameId, Range, RangeId, RowId, SheetId, TextId,
};

pub trait NameFetcherTrait {
Expand All @@ -19,4 +19,22 @@ pub trait NameFetcherTrait {
fn fetch_range(&self, sheet_id: &SheetId, range_id: &RangeId) -> Result<Range, BasicError>;
fn fetch_cube(&self, cube_id: &CubeId) -> Result<Cube, BasicError>;
fn fetch_ext_ref(&mut self, ext_ref_id: &ExtRefId) -> Result<ExtRef, BasicError>;

/// Reverse-lookup a block ref-name from `(sheet_id, block_id)`, used by
/// unparse to render BLOCKREF formulas with the block's *current* name.
/// Returns `None` if the block has no schema bound.
fn fetch_block_ref_name_by_id(&self, _sheet_id: SheetId, _block_id: BlockId) -> Option<String> {
None
}

/// Reverse-lookup a field name. Returns `None` if the schema does not
/// know this field id (e.g., field has been removed since parse time).
fn fetch_block_field_name_by_id(
&self,
_sheet_id: SheetId,
_block_id: BlockId,
_field_id: BlockFieldId,
) -> Option<String> {
None
}
}
1 change: 1 addition & 0 deletions crates/controller/base/src/types/id.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub type Id = u32;
pub type RowId = u32;
pub type ColId = u32;
pub type BlockFieldId = u32;
pub type RangeId = u32;
pub type CubeId = u32;
pub type ExtRefId = u32;
Expand Down
65 changes: 64 additions & 1 deletion crates/controller/parser/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use logisheets_base::{
CellId, ColId, CubeId, ExtBookId, ExtRefId, FuncId, NameId, RangeId, RefAbs, RowId, SheetId,
BlockFieldId, BlockId, CellId, ColId, CubeId, ExtBookId, ExtRefId, FuncId, NameId, RangeId,
RefAbs, RowId, SheetId,
};
use std::hash::{Hash, Hasher};

Expand Down Expand Up @@ -107,6 +108,36 @@ pub enum PureNode {
Func(Func),
Value(Value),
Reference(CellReference),
BlockRef(BlockRefNode),
}

/// Stable, id-keyed AST node for `BLOCKREF / BLOCKREFS / BLOCKREFB / BLOCKREFSB`.
///
/// The textual `ref_name` and `field` arguments are resolved into stable ids at
/// parse time, so dependency tracking (and rename safety) does not depend on
/// the strings the user typed. Both unparse and calc consume these ids
/// directly. The runtime expressions (`key`, `key_condition`, `field_condition`)
/// stay as `Node` because they may reference cells.
#[derive(Debug, Clone)]
pub enum BlockRefNode {
/// Single (BLOCKREF / BLOCKREFB): pick one block-cell by key/field.
Single {
sheet_id: SheetId,
block_id: BlockId,
field_id: BlockFieldId,
/// Whether the source form was `BLOCKREFB` (sheet/block id literals)
/// or `BLOCKREF` (string ref-name). Only affects unparse.
by_block: bool,
key: Box<Node>,
},
/// Multi (BLOCKREFS / BLOCKREFSB): scan a block under filters.
Multi {
sheet_id: SheetId,
block_id: BlockId,
by_block: bool,
key_condition: Box<Node>,
field_condition: Box<Node>,
},
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -136,6 +167,38 @@ impl Node {
pure: PureNode::Value(v),
bracket: self.bracket,
}),
PureNode::BlockRef(BlockRefNode::Single {
sheet_id,
block_id,
field_id,
by_block,
key,
}) => Node {
pure: PureNode::BlockRef(BlockRefNode::Single {
sheet_id,
block_id,
field_id,
by_block,
key: Box::new((*key).accept(visitor)),
}),
bracket: self.bracket,
},
PureNode::BlockRef(BlockRefNode::Multi {
sheet_id,
block_id,
by_block,
key_condition,
field_condition,
}) => Node {
pure: PureNode::BlockRef(BlockRefNode::Multi {
sheet_id,
block_id,
by_block,
key_condition: Box::new((*key_condition).accept(visitor)),
field_condition: Box::new((*field_condition).accept(visitor)),
}),
bracket: self.bracket,
},
_ => self,
}
}
Expand Down
46 changes: 42 additions & 4 deletions crates/controller/parser/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use logisheets_base::block_ref::BlockRefResolverTrait;
use logisheets_base::errors::BasicError;
use logisheets_base::get_book_name::GetBookNameTrait;
use logisheets_base::id_fetcher::{IdFetcherTrait, VertexFetcherTrait};
use logisheets_base::{
Cube, CubeId, ExtBookId, ExtRef, ExtRefId, FuncId, NameId, NormalCellId, Range, RangeId,
SheetId, TextId,
BlockFieldId, BlockId, Cube, CubeId, ExtBookId, ExtRef, ExtRefId, FuncId, NameId, NormalCellId,
Range, RangeId, SheetId, TextId,
};

pub trait ContextTrait: IdFetcherTrait + GetBookNameTrait + VertexFetcherTrait {}
pub trait ContextTrait:
IdFetcherTrait + GetBookNameTrait + VertexFetcherTrait + BlockRefResolverTrait
{
}

pub struct Context<'a, T, F>
where
Expand All @@ -20,9 +24,43 @@ where

impl<'a, T, F> ContextTrait for Context<'a, T, F>
where
T: IdFetcherTrait,
T: IdFetcherTrait + BlockRefResolverTrait,
F: VertexFetcherTrait,
{
}

impl<'a, T, F> BlockRefResolverTrait for Context<'a, T, F>
where
T: IdFetcherTrait + BlockRefResolverTrait,
F: VertexFetcherTrait,
{
fn resolve_block_ref_name(&self, ref_name: &str) -> Option<(SheetId, BlockId)> {
self.id_fetcher.resolve_block_ref_name(ref_name)
}

fn resolve_block_field(
&self,
sheet_id: SheetId,
block_id: BlockId,
field: &str,
) -> Option<BlockFieldId> {
self.id_fetcher
.resolve_block_field(sheet_id, block_id, field)
}

fn fetch_block_ref_name(&self, sheet_id: SheetId, block_id: BlockId) -> Option<String> {
self.id_fetcher.fetch_block_ref_name(sheet_id, block_id)
}

fn fetch_block_field_name(
&self,
sheet_id: SheetId,
block_id: BlockId,
field_id: BlockFieldId,
) -> Option<String> {
self.id_fetcher
.fetch_block_field_name(sheet_id, block_id, field_id)
}
}

impl<'a, T, F> VertexFetcherTrait for Context<'a, T, F>
Expand Down
Loading
Loading