Skip to content

Commit 55e7b5e

Browse files
committed
WIP
1 parent fa75d99 commit 55e7b5e

File tree

20 files changed

+1868
-644
lines changed

20 files changed

+1868
-644
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rs/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ serde_yaml = "0.9"
4040
# both the schema and the input, so we need to depend on that as well, even
4141
# though we don't actually do any JSON serialization and deserialization.
4242
jsonschema = { version = "=0.15.0", default-features = false }
43-
serde_json = "1"
43+
serde_json = { version = "1", features = ["preserve_order"] }
4444

4545
# Used for checking identifier syntax (could be removed if regexes don't end up
4646
# being useful elsewhere too).

rs/src/output/diagnostic.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ pub enum Classification {
246246
#[strum(props(HiddenDescription = "invalid compound vs. simple function name usage"))]
247247
LinkCompoundVsSimpleFunctionName = 3010,
248248

249+
#[strum(props(Description = "discouraged name"))]
250+
LinkDiscouragedName = 3011,
251+
249252
// Type-related diagnostics (group 4).
250253
#[strum(props(HiddenDescription = "type-related diagnostics"))]
251254
Type = 4000,
@@ -302,6 +305,9 @@ pub enum Classification {
302305
#[strum(props(Description = "invalid field name"))]
303306
TypeInvalidFieldName = 4013,
304307

308+
#[strum(props(Description = "unsupported type pattern or derivation construct"))]
309+
TypeDerivationNotSupported = 4014,
310+
305311
// Relation-related diagnostics (group 5).
306312
#[strum(props(HiddenDescription = "relation-related diagnostics"))]
307313
Relation = 5000,

rs/src/output/extension/namespace.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,22 @@ impl<T> ResolutionResult<T> {
531531
self.expect(parse_context, if_not_applicable, |_, _| true, true, false)
532532
}
533533

534+
/// Emits an error if one or more definitions were found for this name
535+
/// resolution, to be used just before defining a new item.
536+
pub fn expect_not_yet_defined(&self, parse_context: &mut context::Context) {
537+
if !self.visible.is_empty() {
538+
traversal::push_diagnostic(
539+
parse_context,
540+
diagnostic::Level::Error,
541+
cause!(
542+
LinkDuplicateDefinition,
543+
"{} is already defined",
544+
self.unresolved_reference
545+
),
546+
);
547+
}
548+
}
549+
534550
/// Silently returns the first matching item, if any. If there are none,
535551
/// this just returns an unresolved reference. Use
536552
/// filter_items().expect_one() to formulate error messages if there are
@@ -567,18 +583,4 @@ impl<T> ResolutionResult<T> {
567583
.next()
568584
.flatten()
569585
}
570-
571-
/// Return an error if one or more definitions were found for this name
572-
/// resolution, to be used just before defining a new item.
573-
pub fn expect_not_yet_defined(&self) -> diagnostic::Result<()> {
574-
if self.visible.is_empty() {
575-
Ok(())
576-
} else {
577-
Err(cause!(
578-
LinkDuplicateDefinition,
579-
"{} is already defined",
580-
self.unresolved_reference
581-
))
582-
}
583-
}
584586
}

rs/src/output/extension/simple/type_class.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,27 @@ use crate::output::type_system::meta::pattern::Pattern;
1212
/// A definition of a user-defined type class.
1313
#[derive(Clone, Debug, PartialEq, Eq, Default)]
1414
pub struct Definition {
15-
/// The underlying structure of the type.
16-
pub structure: Vec<(String, data::class::Simple)>,
15+
/// Name of the type class.
16+
pub name: String,
1717

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

2121
/// Whether or not the last parameter slot is variadic.
2222
pub parameters_variadic: bool,
23+
24+
/// Optional underlying structure of the type.
25+
/// TODO: change to Option<Vec<(String, meta::Program)>> and propagate changes
26+
pub structure: Vec<(String, data::class::Simple)>,
2327
}
2428

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

31-
/// YAML-provided human-readable description of the parameter.
35+
/// Human-readable description of the parameter.
3236
pub description: String,
3337

3438
/// Pattern for type- and bounds-checking parameters bound to this slot.

rs/src/output/type_system/meta/function.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,4 +416,42 @@ impl Function {
416416
}
417417
}
418418
}
419+
420+
/// Returns what type this function evaluates to. If unknown or multiple
421+
/// types can be matched, yield unresolved.
422+
pub fn determine_type(&self, arguments: &[meta::pattern::Value]) -> meta::Type {
423+
match self {
424+
Function::Unresolved => meta::Type::Unresolved,
425+
Function::Not
426+
| Function::And
427+
| Function::Or
428+
| Function::Equal
429+
| Function::NotEqual
430+
| Function::GreaterThan
431+
| Function::LessThan
432+
| Function::GreaterEqual
433+
| Function::LessEqual
434+
| Function::Covers => meta::Type::Boolean,
435+
Function::Negate
436+
| Function::Add
437+
| Function::Subtract
438+
| Function::Multiply
439+
| Function::Divide
440+
| Function::Min
441+
| Function::Max => meta::Type::Integer,
442+
Function::IfThenElse => {
443+
if arguments.len() == 3 {
444+
let a = arguments[1].determine_type();
445+
let b = arguments[2].determine_type();
446+
if a == b {
447+
a
448+
} else {
449+
meta::Type::Unresolved
450+
}
451+
} else {
452+
meta::Type::Unresolved
453+
}
454+
}
455+
}
456+
}
419457
}

rs/src/output/type_system/meta/pattern.rs

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ pub trait Pattern {
4444
/// Evaluates this pattern with a provided context.
4545
fn evaluate_with_context(&self, context: &mut meta::Context)
4646
-> diagnostic::Result<Self::Value>;
47+
48+
/// Returns whether this pattern can be evaluated under ideal conditions.
49+
fn can_evaluate(&self) -> bool;
4750
}
4851

4952
/// Patterns are used wherever a meta::Value is expected, such as for type
@@ -252,6 +255,21 @@ impl Value {
252255
meta::Type::DataType => Value::DataType(None),
253256
}
254257
}
258+
259+
/// Returns what type this pattern matches or evaluates to. If unknown or
260+
/// multiple types can be matched, yield unresolved.
261+
pub fn determine_type(&self) -> meta::Type {
262+
match self {
263+
Value::Unresolved | Value::Any => meta::Type::Unresolved,
264+
Value::Binding(binding) => binding.determine_type(),
265+
Value::Boolean(_) => meta::Type::Boolean,
266+
Value::Integer(_, _) => meta::Type::Integer,
267+
Value::Enum(_) => meta::Type::Enum,
268+
Value::String(_) => meta::Type::String,
269+
Value::DataType(_) => meta::Type::DataType,
270+
Value::Function(function, arguments) => function.determine_type(arguments),
271+
}
272+
}
255273
}
256274

257275
impl Pattern for Value {
@@ -347,6 +365,21 @@ impl Pattern for Value {
347365
Value::Function(func, args) => func.evaluate(context, args),
348366
}
349367
}
368+
369+
fn can_evaluate(&self) -> bool {
370+
match self {
371+
Value::Unresolved => true,
372+
Value::Any => false,
373+
Value::Binding(_) => true,
374+
Value::Boolean(x) => x.is_some(),
375+
Value::Integer(a, b) => a == b,
376+
Value::Enum(x) => x.is_some(),
377+
Value::String(x) => x.is_some(),
378+
Value::DataType(None) => false,
379+
Value::DataType(Some(x)) => x.can_evaluate(),
380+
Value::Function(_, _) => true,
381+
}
382+
}
350383
}
351384

352385
/// Binding matching structure. Four variations exist, as detailed below.
@@ -557,6 +590,16 @@ impl Binding {
557590
))
558591
}
559592
}
593+
594+
/// Returns what type this pattern matches or evaluates to. If unknown or
595+
/// multiple types can be matched, yield unresolved.
596+
pub fn determine_type(&self) -> meta::Type {
597+
if self.nullability.is_some() {
598+
meta::Type::DataType
599+
} else {
600+
meta::Type::Unresolved
601+
}
602+
}
560603
}
561604

562605
/// Data type matching structure.
@@ -639,13 +682,13 @@ impl DataType {
639682
return Ok(false);
640683
}
641684
if !ignore_nullability
642-
&& self
685+
&& !self
643686
.nullable
644687
.match_pattern_with_context(context, &value.nullable().into())?
645688
{
646689
return Ok(false);
647690
}
648-
if self.variation.match_pattern(value.variation())? {
691+
if !self.variation.match_pattern(value.variation())? {
649692
return Ok(false);
650693
}
651694
if let Some(expected) = &self.parameters {
@@ -716,6 +759,15 @@ impl Pattern for DataType {
716759
};
717760
data::new_type(class, nullable, variation, parameters)
718761
}
762+
763+
fn can_evaluate(&self) -> bool {
764+
if let Some(parameters) = &self.parameters {
765+
if !parameters.iter().all(|x| x.can_evaluate()) {
766+
return false;
767+
}
768+
}
769+
self.nullable.can_evaluate() && self.variation.can_evaluate()
770+
}
719771
}
720772

721773
/// Type variation matching structure.
@@ -779,6 +831,13 @@ impl Pattern for Variation {
779831
Variation::Exactly(expected) => Ok(expected.clone()),
780832
}
781833
}
834+
835+
fn can_evaluate(&self) -> bool {
836+
match self {
837+
Variation::Any => false,
838+
Variation::Compatible | Variation::Exactly(_) => true,
839+
}
840+
}
782841
}
783842

784843
/// Pattern for parameters for parameterized types.
@@ -884,4 +943,13 @@ impl Pattern for Parameter {
884943
.transpose()?,
885944
})
886945
}
946+
947+
fn can_evaluate(&self) -> bool {
948+
if let Some(value) = &self.value {
949+
value.can_evaluate()
950+
} else {
951+
// Evaluates to null.
952+
true
953+
}
954+
}
887955
}

rs/src/parse/extensions/simple/builder.rs

Lines changed: 0 additions & 66 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
//! Module providing private helper functions that are not specific to any
4+
//! particular simple extension construct.
5+
6+
use crate::output::diagnostic::Result;
7+
use crate::parse::context;
8+
9+
/// Parser for names given to things.
10+
pub fn parse_name(x: &str, y: &mut context::Context, construct: &str) -> Result<String> {
11+
static IDENTIFIER_RE: once_cell::sync::Lazy<regex::Regex> =
12+
once_cell::sync::Lazy::new(|| regex::Regex::new("[a-zA-Z_][a-zA-Z0-9_\\.]*").unwrap());
13+
14+
if x.is_empty() {
15+
diagnostic!(
16+
y,
17+
Info,
18+
LinkDiscouragedName,
19+
"using the empty string as a {construct} name is not explicitly \
20+
illegal, but probably not a good idea"
21+
);
22+
} else if !IDENTIFIER_RE.is_match(x) {
23+
diagnostic!(
24+
y,
25+
Info,
26+
LinkDiscouragedName,
27+
"it is recommended for {construct} names to case-insensitively \
28+
match [a-z_][a-z0-9_]* for maximum compatibility"
29+
);
30+
} else if x.contains('.') {
31+
diagnostic!(
32+
y,
33+
Info,
34+
LinkDiscouragedName,
35+
"using periods within a {construct} name is not explicitly \
36+
illegal, but probably not a good idea, as they are also used as \
37+
namespace separators for dependencies"
38+
);
39+
}
40+
Ok(x.to_owned())
41+
}

0 commit comments

Comments
 (0)