Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jvanstraten committed Sep 7, 2022
1 parent fa75d99 commit 55e7b5e
Show file tree
Hide file tree
Showing 20 changed files with 1,868 additions and 644 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ serde_yaml = "0.9"
# both the schema and the input, so we need to depend on that as well, even
# though we don't actually do any JSON serialization and deserialization.
jsonschema = { version = "=0.15.0", default-features = false }
serde_json = "1"
serde_json = { version = "1", features = ["preserve_order"] }

# Used for checking identifier syntax (could be removed if regexes don't end up
# being useful elsewhere too).
Expand Down
6 changes: 6 additions & 0 deletions rs/src/output/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ pub enum Classification {
#[strum(props(HiddenDescription = "invalid compound vs. simple function name usage"))]
LinkCompoundVsSimpleFunctionName = 3010,

#[strum(props(Description = "discouraged name"))]
LinkDiscouragedName = 3011,

// Type-related diagnostics (group 4).
#[strum(props(HiddenDescription = "type-related diagnostics"))]
Type = 4000,
Expand Down Expand Up @@ -302,6 +305,9 @@ pub enum Classification {
#[strum(props(Description = "invalid field name"))]
TypeInvalidFieldName = 4013,

#[strum(props(Description = "unsupported type pattern or derivation construct"))]
TypeDerivationNotSupported = 4014,

// Relation-related diagnostics (group 5).
#[strum(props(HiddenDescription = "relation-related diagnostics"))]
Relation = 5000,
Expand Down
30 changes: 16 additions & 14 deletions rs/src/output/extension/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,22 @@ impl<T> ResolutionResult<T> {
self.expect(parse_context, if_not_applicable, |_, _| true, true, false)
}

/// Emits an error if one or more definitions were found for this name
/// resolution, to be used just before defining a new item.
pub fn expect_not_yet_defined(&self, parse_context: &mut context::Context) {
if !self.visible.is_empty() {
traversal::push_diagnostic(
parse_context,
diagnostic::Level::Error,
cause!(
LinkDuplicateDefinition,
"{} is already defined",
self.unresolved_reference
),
);
}
}

/// Silently returns the first matching item, if any. If there are none,
/// this just returns an unresolved reference. Use
/// filter_items().expect_one() to formulate error messages if there are
Expand Down Expand Up @@ -567,18 +583,4 @@ impl<T> ResolutionResult<T> {
.next()
.flatten()
}

/// Return an error if one or more definitions were found for this name
/// resolution, to be used just before defining a new item.
pub fn expect_not_yet_defined(&self) -> diagnostic::Result<()> {
if self.visible.is_empty() {
Ok(())
} else {
Err(cause!(
LinkDuplicateDefinition,
"{} is already defined",
self.unresolved_reference
))
}
}
}
12 changes: 8 additions & 4 deletions rs/src/output/extension/simple/type_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@ use crate::output::type_system::meta::pattern::Pattern;
/// A definition of a user-defined type class.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct Definition {
/// The underlying structure of the type.
pub structure: Vec<(String, data::class::Simple)>,
/// Name of the type class.
pub name: String,

/// The parameters expected by the data type.
pub parameter_slots: Vec<ParameterSlot>,

/// Whether or not the last parameter slot is variadic.
pub parameters_variadic: bool,

/// Optional underlying structure of the type.
/// TODO: change to Option<Vec<(String, meta::Program)>> and propagate changes
pub structure: Vec<(String, data::class::Simple)>,
}

/// A parameter slot for a user-defined data type.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ParameterSlot {
/// YAML-provided name of the parameter.
/// Name of the parameter.
pub name: String,

/// YAML-provided human-readable description of the parameter.
/// Human-readable description of the parameter.
pub description: String,

/// Pattern for type- and bounds-checking parameters bound to this slot.
Expand Down
38 changes: 38 additions & 0 deletions rs/src/output/type_system/meta/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,4 +416,42 @@ impl Function {
}
}
}

/// Returns what type this function evaluates to. If unknown or multiple
/// types can be matched, yield unresolved.
pub fn determine_type(&self, arguments: &[meta::pattern::Value]) -> meta::Type {
match self {
Function::Unresolved => meta::Type::Unresolved,
Function::Not
| Function::And
| Function::Or
| Function::Equal
| Function::NotEqual
| Function::GreaterThan
| Function::LessThan
| Function::GreaterEqual
| Function::LessEqual
| Function::Covers => meta::Type::Boolean,
Function::Negate
| Function::Add
| Function::Subtract
| Function::Multiply
| Function::Divide
| Function::Min
| Function::Max => meta::Type::Integer,
Function::IfThenElse => {
if arguments.len() == 3 {
let a = arguments[1].determine_type();
let b = arguments[2].determine_type();
if a == b {
a
} else {
meta::Type::Unresolved
}
} else {
meta::Type::Unresolved
}
}
}
}
}
72 changes: 70 additions & 2 deletions rs/src/output/type_system/meta/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pub trait Pattern {
/// Evaluates this pattern with a provided context.
fn evaluate_with_context(&self, context: &mut meta::Context)
-> diagnostic::Result<Self::Value>;

/// Returns whether this pattern can be evaluated under ideal conditions.
fn can_evaluate(&self) -> bool;
}

/// Patterns are used wherever a meta::Value is expected, such as for type
Expand Down Expand Up @@ -252,6 +255,21 @@ impl Value {
meta::Type::DataType => Value::DataType(None),
}
}

/// Returns what type this pattern matches or evaluates to. If unknown or
/// multiple types can be matched, yield unresolved.
pub fn determine_type(&self) -> meta::Type {
match self {
Value::Unresolved | Value::Any => meta::Type::Unresolved,
Value::Binding(binding) => binding.determine_type(),
Value::Boolean(_) => meta::Type::Boolean,
Value::Integer(_, _) => meta::Type::Integer,
Value::Enum(_) => meta::Type::Enum,
Value::String(_) => meta::Type::String,
Value::DataType(_) => meta::Type::DataType,
Value::Function(function, arguments) => function.determine_type(arguments),
}
}
}

impl Pattern for Value {
Expand Down Expand Up @@ -347,6 +365,21 @@ impl Pattern for Value {
Value::Function(func, args) => func.evaluate(context, args),
}
}

fn can_evaluate(&self) -> bool {
match self {
Value::Unresolved => true,
Value::Any => false,
Value::Binding(_) => true,
Value::Boolean(x) => x.is_some(),
Value::Integer(a, b) => a == b,
Value::Enum(x) => x.is_some(),
Value::String(x) => x.is_some(),
Value::DataType(None) => false,
Value::DataType(Some(x)) => x.can_evaluate(),
Value::Function(_, _) => true,
}
}
}

/// Binding matching structure. Four variations exist, as detailed below.
Expand Down Expand Up @@ -557,6 +590,16 @@ impl Binding {
))
}
}

/// Returns what type this pattern matches or evaluates to. If unknown or
/// multiple types can be matched, yield unresolved.
pub fn determine_type(&self) -> meta::Type {
if self.nullability.is_some() {
meta::Type::DataType
} else {
meta::Type::Unresolved
}
}
}

/// Data type matching structure.
Expand Down Expand Up @@ -639,13 +682,13 @@ impl DataType {
return Ok(false);
}
if !ignore_nullability
&& self
&& !self
.nullable
.match_pattern_with_context(context, &value.nullable().into())?
{
return Ok(false);
}
if self.variation.match_pattern(value.variation())? {
if !self.variation.match_pattern(value.variation())? {
return Ok(false);
}
if let Some(expected) = &self.parameters {
Expand Down Expand Up @@ -716,6 +759,15 @@ impl Pattern for DataType {
};
data::new_type(class, nullable, variation, parameters)
}

fn can_evaluate(&self) -> bool {
if let Some(parameters) = &self.parameters {
if !parameters.iter().all(|x| x.can_evaluate()) {
return false;
}
}
self.nullable.can_evaluate() && self.variation.can_evaluate()
}
}

/// Type variation matching structure.
Expand Down Expand Up @@ -779,6 +831,13 @@ impl Pattern for Variation {
Variation::Exactly(expected) => Ok(expected.clone()),
}
}

fn can_evaluate(&self) -> bool {
match self {
Variation::Any => false,
Variation::Compatible | Variation::Exactly(_) => true,
}
}
}

/// Pattern for parameters for parameterized types.
Expand Down Expand Up @@ -884,4 +943,13 @@ impl Pattern for Parameter {
.transpose()?,
})
}

fn can_evaluate(&self) -> bool {
if let Some(value) = &self.value {
value.can_evaluate()
} else {
// Evaluates to null.
true
}
}
}
66 changes: 0 additions & 66 deletions rs/src/parse/extensions/simple/builder.rs

This file was deleted.

41 changes: 41 additions & 0 deletions rs/src/parse/extensions/simple/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0

//! Module providing private helper functions that are not specific to any
//! particular simple extension construct.

use crate::output::diagnostic::Result;
use crate::parse::context;

/// Parser for names given to things.
pub fn parse_name(x: &str, y: &mut context::Context, construct: &str) -> Result<String> {
static IDENTIFIER_RE: once_cell::sync::Lazy<regex::Regex> =
once_cell::sync::Lazy::new(|| regex::Regex::new("[a-zA-Z_][a-zA-Z0-9_\\.]*").unwrap());

if x.is_empty() {
diagnostic!(
y,
Info,
LinkDiscouragedName,
"using the empty string as a {construct} name is not explicitly \
illegal, but probably not a good idea"
);
} else if !IDENTIFIER_RE.is_match(x) {
diagnostic!(
y,
Info,
LinkDiscouragedName,
"it is recommended for {construct} names to case-insensitively \
match [a-z_][a-z0-9_]* for maximum compatibility"
);
} else if x.contains('.') {
diagnostic!(
y,
Info,
LinkDiscouragedName,
"using periods within a {construct} name is not explicitly \
illegal, but probably not a good idea, as they are also used as \
namespace separators for dependencies"
);
}
Ok(x.to_owned())
}
Loading

0 comments on commit 55e7b5e

Please sign in to comment.