use super::document::{
CsvRow, DocumentRecord, JsonProperty, LocalisationDefinition, LocalisationDuplicate,
ParseIssue, ResourceReference, UiDefinition,
};
use super::{GamePathBuf, MaybeScope, ScopeSet};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
Hash,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub enum SymbolKind {
ScriptedEffect,
ScriptedTrigger,
Event,
Decision,
DiplomaticAction,
TriggeredModifier,
}
impl SymbolKind {
pub fn as_str(self) -> &'static str {
match self {
SymbolKind::ScriptedEffect => "scripted_effect",
SymbolKind::ScriptedTrigger => "scripted_trigger",
SymbolKind::Event => "event",
SymbolKind::Decision => "decision",
SymbolKind::DiplomaticAction => "diplomatic_action",
SymbolKind::TriggeredModifier => "triggered_modifier",
}
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub enum ScopeKind {
File,
Event,
Decision,
ScriptedEffect,
Trigger,
Effect,
Loop,
AliasBlock,
Block,
}
#[derive(
Clone,
Debug,
Eq,
PartialEq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct SourceSpan {
pub line: usize,
pub column: usize,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ScopeNode {
pub id: usize,
pub kind: ScopeKind,
pub parent: Option<usize>,
pub this_type: MaybeScope,
pub aliases: BTreeMap<String, MaybeScope>,
pub mod_id: String,
pub path: GamePathBuf,
pub span: SourceSpan,
#[serde(default)]
pub key: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SymbolDefinition {
pub kind: SymbolKind,
pub name: String,
pub module: String,
pub local_name: String,
pub mod_id: String,
pub path: GamePathBuf,
pub line: usize,
pub column: usize,
pub scope_id: usize,
pub declared_this_type: MaybeScope,
pub inferred_this_type: MaybeScope,
#[serde(default)]
pub inferred_this_mask: ScopeSet,
#[serde(default)]
pub inferred_from_mask: ScopeSet,
#[serde(default)]
pub inferred_root_mask: ScopeSet,
pub required_params: Vec<String>,
#[serde(default)]
pub optional_params: Vec<String>,
#[serde(default)]
pub param_contract: Option<ParamContract>,
#[serde(default)]
pub scope_param_names: Vec<String>,
}
#[derive(
Clone,
Debug,
Eq,
PartialEq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct ParamBinding {
pub name: String,
pub value: String,
}
#[derive(
Clone,
Debug,
Eq,
PartialEq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct ConditionalParamRule {
pub when_present: String,
pub requires_any_of: Vec<String>,
}
#[derive(
Clone,
Debug,
Eq,
PartialEq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct ParamContract {
pub required_all: Vec<String>,
pub optional: Vec<String>,
pub one_of_groups: Vec<Vec<String>>,
pub conditional_required: Vec<ConditionalParamRule>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SymbolReference {
pub kind: SymbolKind,
pub name: String,
pub module: String,
pub mod_id: String,
pub path: GamePathBuf,
pub line: usize,
pub column: usize,
pub scope_id: usize,
pub provided_params: Vec<String>,
pub param_bindings: Vec<ParamBinding>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AliasUsage {
pub alias: String,
pub mod_id: String,
pub path: GamePathBuf,
pub line: usize,
pub column: usize,
pub scope_id: usize,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct KeyUsage {
pub key: String,
pub mod_id: String,
pub path: GamePathBuf,
pub line: usize,
pub column: usize,
pub scope_id: usize,
pub this_type: MaybeScope,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ScalarAssignment {
pub key: String,
pub value: String,
pub mod_id: String,
pub path: GamePathBuf,
pub line: usize,
pub column: usize,
pub scope_id: usize,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct SemanticIndex {
pub documents: Vec<DocumentRecord>,
pub scopes: Vec<ScopeNode>,
pub definitions: Vec<SymbolDefinition>,
pub references: Vec<SymbolReference>,
pub alias_usages: Vec<AliasUsage>,
pub key_usages: Vec<KeyUsage>,
pub scalar_assignments: Vec<ScalarAssignment>,
pub localisation_definitions: Vec<LocalisationDefinition>,
pub localisation_duplicates: Vec<LocalisationDuplicate>,
pub ui_definitions: Vec<UiDefinition>,
pub resource_references: Vec<ResourceReference>,
pub csv_rows: Vec<CsvRow>,
pub json_properties: Vec<JsonProperty>,
pub parse_issues: Vec<ParseIssue>,
}
impl SemanticIndex {
pub fn append(&mut self, mut other: Self) {
let offset = self.scopes.len();
for scope in &mut other.scopes {
scope.id += offset;
if let Some(parent) = scope.parent {
scope.parent = Some(parent + offset);
}
}
for definition in &mut other.definitions {
definition.scope_id += offset;
}
for reference in &mut other.references {
reference.scope_id += offset;
}
for alias in &mut other.alias_usages {
alias.scope_id += offset;
}
for usage in &mut other.key_usages {
usage.scope_id += offset;
}
for assignment in &mut other.scalar_assignments {
assignment.scope_id += offset;
}
self.scopes.extend(other.scopes);
self.definitions.extend(other.definitions);
self.references.extend(other.references);
self.alias_usages.extend(other.alias_usages);
self.key_usages.extend(other.key_usages);
self.scalar_assignments.extend(other.scalar_assignments);
self.documents.extend(other.documents);
self.localisation_definitions
.extend(other.localisation_definitions);
self.localisation_duplicates
.extend(other.localisation_duplicates);
self.ui_definitions.extend(other.ui_definitions);
self.resource_references.extend(other.resource_references);
self.csv_rows.extend(other.csv_rows);
self.json_properties.extend(other.json_properties);
self.parse_issues.extend(other.parse_issues);
}
}
#[cfg(test)]
pub const SEMANTIC_RECORD_KINDS: [&str; 14] = [
"document",
"scope",
"definition",
"reference",
"alias usage",
"key usage",
"scalar assignment",
"localisation definition",
"localisation duplicate",
"ui definition",
"resource reference",
"csv row",
"json property",
"parse issue",
];
#[cfg(test)]
pub fn index_with_one_record_of_each_kind(path: impl Fn(usize) -> GamePathBuf) -> SemanticIndex {
let mod_id = || "mod-a".to_string();
SemanticIndex {
documents: vec![DocumentRecord {
mod_id: mod_id(),
path: path(0),
family: super::DocumentFamily::Clausewitz,
parse_ok: true,
}],
scopes: vec![ScopeNode {
id: 0,
kind: ScopeKind::File,
parent: None,
this_type: MaybeScope::Unknown,
aliases: BTreeMap::new(),
mod_id: mod_id(),
path: path(1),
span: SourceSpan { line: 1, column: 1 },
key: String::new(),
}],
definitions: vec![SymbolDefinition {
kind: SymbolKind::ScriptedEffect,
name: "eu4::m::defined".to_string(),
module: "m".to_string(),
local_name: "defined".to_string(),
mod_id: mod_id(),
path: path(2),
line: 1,
column: 1,
scope_id: 0,
declared_this_type: MaybeScope::Unknown,
inferred_this_type: MaybeScope::Unknown,
inferred_this_mask: ScopeSet::default(),
inferred_from_mask: ScopeSet::default(),
inferred_root_mask: ScopeSet::default(),
required_params: Vec::new(),
optional_params: Vec::new(),
param_contract: None,
scope_param_names: Vec::new(),
}],
references: vec![SymbolReference {
kind: SymbolKind::ScriptedEffect,
name: "eu4::m::referenced".to_string(),
module: "m".to_string(),
mod_id: mod_id(),
path: path(3),
line: 1,
column: 1,
scope_id: 0,
provided_params: Vec::new(),
param_bindings: Vec::new(),
}],
alias_usages: vec![AliasUsage {
alias: "ROOT".to_string(),
mod_id: mod_id(),
path: path(4),
line: 1,
column: 1,
scope_id: 0,
}],
key_usages: vec![KeyUsage {
key: "used_key".to_string(),
mod_id: mod_id(),
path: path(5),
line: 1,
column: 1,
scope_id: 0,
this_type: MaybeScope::Unknown,
}],
scalar_assignments: vec![ScalarAssignment {
key: "assigned".to_string(),
value: "1".to_string(),
mod_id: mod_id(),
path: path(6),
line: 1,
column: 1,
scope_id: 0,
}],
localisation_definitions: vec![LocalisationDefinition {
key: "LOC_KEY".to_string(),
mod_id: mod_id(),
path: path(7),
line: 1,
column: 1,
}],
localisation_duplicates: vec![LocalisationDuplicate {
key: "LOC_DUP".to_string(),
mod_id: mod_id(),
path: path(8),
first_line: 1,
duplicate_line: 2,
}],
ui_definitions: vec![UiDefinition {
name: "ui_name".to_string(),
mod_id: mod_id(),
path: path(9),
line: 1,
column: 1,
}],
resource_references: vec![ResourceReference {
key: "texturefile".to_string(),
value: "gfx/a.dds".to_string(),
mod_id: mod_id(),
path: path(10),
line: 1,
column: 1,
}],
csv_rows: vec![CsvRow {
identity: "row".to_string(),
mod_id: mod_id(),
path: path(11),
line: 1,
column: 1,
}],
json_properties: vec![JsonProperty {
key_path: "a.b".to_string(),
mod_id: mod_id(),
path: path(12),
line: 1,
column: 1,
}],
parse_issues: vec![ParseIssue {
mod_id: mod_id(),
path: path(13),
line: 1,
column: 1,
message: "issue".to_string(),
repair: None,
isolation: None,
}],
}
}