Support types in the standard library (#5651)
* Parse an unparse type decls (and refactor impl attributes slightly) Signed-off-by: Nick Cameron <nrc@ncameron.org> * Remove special treatment of geometric types from parser and executor Signed-off-by: Nick Cameron <nrc@ncameron.org> * Generate docs for std types Signed-off-by: Nick Cameron <nrc@ncameron.org> * Hover tool-tips for types and fixup the frontend Signed-off-by: Nick Cameron <nrc@ncameron.org> * Fixes Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -5,8 +5,8 @@ use crate::parsing::ast::types::{
|
||||
CallExpression, CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression,
|
||||
Identifier, IfExpression, ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression,
|
||||
Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, ObjectExpression, ObjectProperty,
|
||||
Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, TagDeclarator, Type, UnaryExpression,
|
||||
VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, TagDeclarator, Type,
|
||||
TypeDeclaration, UnaryExpression, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
};
|
||||
|
||||
/// Position-independent digest of the AST node.
|
||||
@ -113,6 +113,7 @@ impl BodyItem {
|
||||
BodyItem::ImportStatement(s) => s.compute_digest(),
|
||||
BodyItem::ExpressionStatement(es) => es.compute_digest(),
|
||||
BodyItem::VariableDeclaration(vs) => vs.compute_digest(),
|
||||
BodyItem::TypeDeclaration(t) => t.compute_digest(),
|
||||
BodyItem::ReturnStatement(rs) => rs.compute_digest(),
|
||||
});
|
||||
|
||||
@ -191,11 +192,11 @@ impl Type {
|
||||
match self {
|
||||
Type::Primitive(prim) => {
|
||||
hasher.update(b"FnArgType::Primitive");
|
||||
hasher.update(prim.digestable_id())
|
||||
hasher.update(prim.compute_digest())
|
||||
}
|
||||
Type::Array(prim) => {
|
||||
hasher.update(b"FnArgType::Array");
|
||||
hasher.update(prim.digestable_id())
|
||||
hasher.update(prim.compute_digest())
|
||||
}
|
||||
Type::Object { properties } => {
|
||||
hasher.update(b"FnArgType::Object");
|
||||
@ -210,6 +211,21 @@ impl Type {
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimitiveType {
|
||||
pub fn compute_digest(&mut self) -> Digest {
|
||||
let mut hasher = Sha256::new();
|
||||
match self {
|
||||
PrimitiveType::Named(id) => hasher.update(id.compute_digest()),
|
||||
PrimitiveType::String => hasher.update(b"string"),
|
||||
PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),
|
||||
PrimitiveType::Boolean => hasher.update(b"bool"),
|
||||
PrimitiveType::Tag => hasher.update(b"tag"),
|
||||
}
|
||||
|
||||
hasher.finalize().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Parameter {
|
||||
compute_digest!(|slf, hasher| {
|
||||
hasher.update(slf.identifier.compute_digest());
|
||||
@ -275,6 +291,18 @@ impl VariableDeclaration {
|
||||
});
|
||||
}
|
||||
|
||||
impl TypeDeclaration {
|
||||
compute_digest!(|slf, hasher| {
|
||||
hasher.update(slf.name.compute_digest());
|
||||
if let Some(args) = &mut slf.args {
|
||||
hasher.update([1]);
|
||||
for a in args {
|
||||
hasher.update(a.compute_digest());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl VariableKind {
|
||||
fn digestable_id(&self) -> [u8; 1] {
|
||||
match self {
|
||||
|
@ -13,6 +13,7 @@ impl BodyItem {
|
||||
BodyItem::ImportStatement(stmt) => stmt.module_id,
|
||||
BodyItem::ExpressionStatement(expression_statement) => expression_statement.module_id,
|
||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.module_id,
|
||||
BodyItem::TypeDeclaration(ty_declaration) => ty_declaration.module_id,
|
||||
BodyItem::ReturnStatement(return_statement) => return_statement.module_id,
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ pub async fn modify_ast_for_sketch(
|
||||
let constraint_level = match ast_sketch {
|
||||
super::types::Definition::Variable(var) => var.get_constraint_level(),
|
||||
super::types::Definition::Import(import) => import.get_constraint_level(),
|
||||
super::types::Definition::Type(_) => ConstraintLevel::Ignore {
|
||||
source_ranges: Vec::new(),
|
||||
},
|
||||
};
|
||||
match &constraint_level {
|
||||
ConstraintLevel::None { source_ranges: _ } => {}
|
||||
|
@ -38,6 +38,7 @@ mod none;
|
||||
pub enum Definition<'a> {
|
||||
Variable(&'a VariableDeclarator),
|
||||
Import(NodeRef<'a, ImportStatement>),
|
||||
Type(NodeRef<'a, TypeDeclaration>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
||||
@ -54,18 +55,6 @@ pub struct Node<T> {
|
||||
pub outer_attrs: NodeList<Annotation>,
|
||||
}
|
||||
|
||||
impl<T> Node<T> {
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
source_range: SourceRange::new(self.start, self.end, self.module_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, pos: usize) -> bool {
|
||||
self.start <= pos && pos <= self.end
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JsonSchema> schemars::JsonSchema for Node<T> {
|
||||
fn schema_name() -> String {
|
||||
T::schema_name()
|
||||
@ -126,6 +115,26 @@ impl<T> Node<T> {
|
||||
pub fn as_source_ranges(&self) -> Vec<SourceRange> {
|
||||
vec![self.as_source_range()]
|
||||
}
|
||||
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
source_range: SourceRange::new(self.start, self.end, self.module_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, pos: usize) -> bool {
|
||||
self.start <= pos && pos <= self.end
|
||||
}
|
||||
|
||||
pub fn map<U>(self, f: fn(T) -> U) -> Node<U> {
|
||||
Node {
|
||||
inner: f(self.inner),
|
||||
start: self.start,
|
||||
end: self.end,
|
||||
module_id: self.module_id,
|
||||
outer_attrs: self.outer_attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Node<T> {
|
||||
@ -352,7 +361,7 @@ impl Program {
|
||||
|
||||
// Recurse over the item.
|
||||
match item {
|
||||
BodyItem::ImportStatement(_) => None,
|
||||
BodyItem::ImportStatement(_) | BodyItem::TypeDeclaration(_) => None,
|
||||
BodyItem::ExpressionStatement(expression_statement) => Some(&expression_statement.expression),
|
||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.get_expr_for_position(pos),
|
||||
BodyItem::ReturnStatement(return_statement) => Some(&return_statement.argument),
|
||||
@ -373,6 +382,7 @@ impl Program {
|
||||
Some(BodyItem::VariableDeclaration(variable_declaration)) => {
|
||||
variable_declaration.get_expr_for_position(pos)
|
||||
}
|
||||
Some(BodyItem::TypeDeclaration(_)) => None,
|
||||
Some(BodyItem::ReturnStatement(return_statement)) => Some(&return_statement.argument),
|
||||
None => return false,
|
||||
};
|
||||
@ -395,7 +405,7 @@ impl Program {
|
||||
// We only care about the top level things in the program.
|
||||
for item in &self.body {
|
||||
match item {
|
||||
BodyItem::ImportStatement(_) => continue,
|
||||
BodyItem::ImportStatement(_) | BodyItem::TypeDeclaration(_) => continue,
|
||||
BodyItem::ExpressionStatement(expression_statement) => {
|
||||
if let Some(folding_range) = expression_statement.expression.get_lsp_folding_range() {
|
||||
ranges.push(folding_range)
|
||||
@ -425,16 +435,13 @@ impl Program {
|
||||
break;
|
||||
}
|
||||
}
|
||||
BodyItem::ExpressionStatement(_expression_statement) => {
|
||||
continue;
|
||||
}
|
||||
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
|
||||
if let Some(var_old_name) = variable_declaration.rename_symbol(new_name, pos) {
|
||||
old_name = Some(var_old_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
BodyItem::ReturnStatement(_return_statement) => continue,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -458,6 +465,7 @@ impl Program {
|
||||
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
|
||||
variable_declaration.get_mut_expr_for_position(pos)
|
||||
}
|
||||
BodyItem::TypeDeclaration(_) => None,
|
||||
BodyItem::ReturnStatement(ref mut return_statement) => Some(&mut return_statement.argument),
|
||||
};
|
||||
|
||||
@ -483,16 +491,17 @@ impl Program {
|
||||
fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
||||
for item in &mut self.body {
|
||||
match item {
|
||||
BodyItem::ImportStatement(ref mut stmt) => {
|
||||
BodyItem::ImportStatement(stmt) => {
|
||||
stmt.rename_identifiers(old_name, new_name);
|
||||
}
|
||||
BodyItem::ExpressionStatement(ref mut expression_statement) => {
|
||||
BodyItem::ExpressionStatement(expression_statement) => {
|
||||
expression_statement.expression.rename_identifiers(old_name, new_name);
|
||||
}
|
||||
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
|
||||
BodyItem::VariableDeclaration(variable_declaration) => {
|
||||
variable_declaration.rename_identifiers(old_name, new_name);
|
||||
}
|
||||
BodyItem::ReturnStatement(ref mut return_statement) => {
|
||||
BodyItem::TypeDeclaration(_) => {}
|
||||
BodyItem::ReturnStatement(return_statement) => {
|
||||
return_statement.argument.rename_identifiers(old_name, new_name);
|
||||
}
|
||||
}
|
||||
@ -506,7 +515,7 @@ impl Program {
|
||||
BodyItem::ImportStatement(_) => {
|
||||
continue;
|
||||
}
|
||||
BodyItem::ExpressionStatement(_expression_statement) => {
|
||||
BodyItem::ExpressionStatement(_) => {
|
||||
continue;
|
||||
}
|
||||
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
|
||||
@ -515,7 +524,10 @@ impl Program {
|
||||
return;
|
||||
}
|
||||
}
|
||||
BodyItem::ReturnStatement(_return_statement) => continue,
|
||||
BodyItem::TypeDeclaration(_) => {
|
||||
continue;
|
||||
}
|
||||
BodyItem::ReturnStatement(_) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -531,6 +543,7 @@ impl Program {
|
||||
BodyItem::VariableDeclaration(ref mut variable_declaration) => {
|
||||
variable_declaration.replace_value(source_range, new_value.clone())
|
||||
}
|
||||
BodyItem::TypeDeclaration(_) => {}
|
||||
BodyItem::ReturnStatement(ref mut return_statement) => {
|
||||
return_statement.argument.replace_value(source_range, new_value.clone())
|
||||
}
|
||||
@ -555,6 +568,11 @@ impl Program {
|
||||
return Some(Definition::Variable(&variable_declaration.declaration));
|
||||
}
|
||||
}
|
||||
BodyItem::TypeDeclaration(ty_declaration) => {
|
||||
if ty_declaration.name.name == name {
|
||||
return Some(Definition::Type(ty_declaration));
|
||||
}
|
||||
}
|
||||
BodyItem::ReturnStatement(_return_statement) => continue,
|
||||
}
|
||||
}
|
||||
@ -588,6 +606,7 @@ pub enum BodyItem {
|
||||
ImportStatement(BoxNode<ImportStatement>),
|
||||
ExpressionStatement(Node<ExpressionStatement>),
|
||||
VariableDeclaration(BoxNode<VariableDeclaration>),
|
||||
TypeDeclaration(BoxNode<TypeDeclaration>),
|
||||
ReturnStatement(Node<ReturnStatement>),
|
||||
}
|
||||
|
||||
@ -597,6 +616,7 @@ impl BodyItem {
|
||||
BodyItem::ImportStatement(stmt) => stmt.start,
|
||||
BodyItem::ExpressionStatement(expression_statement) => expression_statement.start,
|
||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.start,
|
||||
BodyItem::TypeDeclaration(ty_declaration) => ty_declaration.start,
|
||||
BodyItem::ReturnStatement(return_statement) => return_statement.start,
|
||||
}
|
||||
}
|
||||
@ -606,6 +626,7 @@ impl BodyItem {
|
||||
BodyItem::ImportStatement(stmt) => stmt.end,
|
||||
BodyItem::ExpressionStatement(expression_statement) => expression_statement.end,
|
||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.end,
|
||||
BodyItem::TypeDeclaration(ty_declaration) => ty_declaration.end,
|
||||
BodyItem::ReturnStatement(return_statement) => return_statement.end,
|
||||
}
|
||||
}
|
||||
@ -615,6 +636,7 @@ impl BodyItem {
|
||||
BodyItem::ImportStatement(node) => node.outer_attrs = attr,
|
||||
BodyItem::ExpressionStatement(node) => node.outer_attrs = attr,
|
||||
BodyItem::VariableDeclaration(node) => node.outer_attrs = attr,
|
||||
BodyItem::TypeDeclaration(ty_declaration) => ty_declaration.outer_attrs = attr,
|
||||
BodyItem::ReturnStatement(node) => node.outer_attrs = attr,
|
||||
}
|
||||
}
|
||||
@ -624,6 +646,7 @@ impl BodyItem {
|
||||
BodyItem::ImportStatement(node) => &node.outer_attrs,
|
||||
BodyItem::ExpressionStatement(node) => &node.outer_attrs,
|
||||
BodyItem::VariableDeclaration(node) => &node.outer_attrs,
|
||||
BodyItem::TypeDeclaration(ty_declaration) => &ty_declaration.outer_attrs,
|
||||
BodyItem::ReturnStatement(node) => &node.outer_attrs,
|
||||
}
|
||||
}
|
||||
@ -633,6 +656,7 @@ impl BodyItem {
|
||||
BodyItem::ImportStatement(node) => &mut node.outer_attrs,
|
||||
BodyItem::ExpressionStatement(node) => &mut node.outer_attrs,
|
||||
BodyItem::VariableDeclaration(node) => &mut node.outer_attrs,
|
||||
BodyItem::TypeDeclaration(ty_declaration) => &mut ty_declaration.outer_attrs,
|
||||
BodyItem::ReturnStatement(node) => &mut node.outer_attrs,
|
||||
}
|
||||
}
|
||||
@ -1765,6 +1789,20 @@ impl ItemVisibility {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub struct TypeDeclaration {
|
||||
pub name: Node<Identifier>,
|
||||
pub args: Option<NodeList<Identifier>>,
|
||||
#[serde(default, skip_serializing_if = "ItemVisibility::is_default")]
|
||||
pub visibility: ItemVisibility,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub digest: Option<Digest>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
@ -2832,7 +2870,8 @@ impl PipeExpression {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum PrimitiveType {
|
||||
@ -2845,39 +2884,16 @@ pub enum PrimitiveType {
|
||||
Boolean,
|
||||
/// A tag.
|
||||
Tag,
|
||||
/// A sketch type.
|
||||
Sketch,
|
||||
/// A sketch surface type.
|
||||
SketchSurface,
|
||||
/// An solid type.
|
||||
Solid,
|
||||
/// A plane.
|
||||
Plane,
|
||||
/// An identifier used as a type (not really a primitive type, but whatever).
|
||||
Named(Node<Identifier>),
|
||||
}
|
||||
|
||||
impl PrimitiveType {
|
||||
pub fn digestable_id(&self) -> &[u8] {
|
||||
match self {
|
||||
PrimitiveType::String => b"string",
|
||||
PrimitiveType::Number(suffix) => suffix.digestable_id(),
|
||||
PrimitiveType::Boolean => b"bool",
|
||||
PrimitiveType::Tag => b"tag",
|
||||
PrimitiveType::Sketch => b"Sketch",
|
||||
PrimitiveType::SketchSurface => b"SketchSurface",
|
||||
PrimitiveType::Solid => b"Solid",
|
||||
PrimitiveType::Plane => b"Plane",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str, suffix: Option<NumericSuffix>) -> Option<Self> {
|
||||
pub fn primitive_from_str(s: &str, suffix: Option<NumericSuffix>) -> Option<Self> {
|
||||
match (s, suffix) {
|
||||
("string", None) => Some(PrimitiveType::String),
|
||||
("bool", None) => Some(PrimitiveType::Boolean),
|
||||
("tag", None) => Some(PrimitiveType::Tag),
|
||||
("Sketch", None) => Some(PrimitiveType::Sketch),
|
||||
("SketchSurface", None) => Some(PrimitiveType::SketchSurface),
|
||||
("Solid", None) => Some(PrimitiveType::Solid),
|
||||
("Plane", None) => Some(PrimitiveType::Plane),
|
||||
("number", None) => Some(PrimitiveType::Number(NumericSuffix::None)),
|
||||
("number", Some(s)) => Some(PrimitiveType::Number(s)),
|
||||
_ => None,
|
||||
@ -2898,10 +2914,7 @@ impl fmt::Display for PrimitiveType {
|
||||
PrimitiveType::String => write!(f, "string"),
|
||||
PrimitiveType::Boolean => write!(f, "bool"),
|
||||
PrimitiveType::Tag => write!(f, "tag"),
|
||||
PrimitiveType::Sketch => write!(f, "Sketch"),
|
||||
PrimitiveType::SketchSurface => write!(f, "SketchSurface"),
|
||||
PrimitiveType::Solid => write!(f, "Solid"),
|
||||
PrimitiveType::Plane => write!(f, "Plane"),
|
||||
PrimitiveType::Named(n) => write!(f, "{}", n.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ use crate::{
|
||||
ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue, MemberExpression,
|
||||
MemberObject, Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty,
|
||||
Parameter, PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, Shebang,
|
||||
TagDeclarator, Type, UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
TagDeclarator, Type, TypeDeclaration, UnaryExpression, UnaryOperator, VariableDeclaration,
|
||||
VariableDeclarator, VariableKind,
|
||||
},
|
||||
math::BinaryExpressionToken,
|
||||
token::{Token, TokenSlice, TokenType},
|
||||
@ -1175,11 +1176,10 @@ fn function_decl(i: &mut TokenSlice) -> PResult<(Node<FunctionExpression>, bool)
|
||||
/// E.g. `person.name`
|
||||
fn member_expression_dot(i: &mut TokenSlice) -> PResult<(LiteralIdentifier, usize, bool)> {
|
||||
period.parse_next(i)?;
|
||||
let property = alt((
|
||||
sketch_keyword.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
nameable_identifier.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
))
|
||||
.parse_next(i)?;
|
||||
let property = nameable_identifier
|
||||
.map(Box::new)
|
||||
.map(LiteralIdentifier::Identifier)
|
||||
.parse_next(i)?;
|
||||
let end = property.end();
|
||||
Ok((property, end, false))
|
||||
}
|
||||
@ -1188,7 +1188,6 @@ fn member_expression_dot(i: &mut TokenSlice) -> PResult<(LiteralIdentifier, usiz
|
||||
fn member_expression_subscript(i: &mut TokenSlice) -> PResult<(LiteralIdentifier, usize, bool)> {
|
||||
let _ = open_bracket.parse_next(i)?;
|
||||
let property = alt((
|
||||
sketch_keyword.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
literal.map(LiteralIdentifier::Literal),
|
||||
nameable_identifier.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
))
|
||||
@ -1330,7 +1329,9 @@ fn body_items_within_function(i: &mut TokenSlice) -> PResult<WithinFunction> {
|
||||
// Any of the body item variants, each of which can optionally be followed by a comment.
|
||||
// If there is a comment, it may be preceded by whitespace.
|
||||
let item = dispatch! {peek(any);
|
||||
token if token.visibility_keyword().is_some() => (alt((declaration.map(BodyItem::VariableDeclaration), import_stmt.map(BodyItem::ImportStatement))), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||
token if token.visibility_keyword().is_some() => (alt((import_stmt.map(BodyItem::ImportStatement), ty_decl.map(BodyItem::TypeDeclaration), declaration.map(BodyItem::VariableDeclaration))), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||
token if token.value == "type" && matches!(token.token_type, TokenType::Keyword) =>
|
||||
(ty_decl.map(BodyItem::TypeDeclaration), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||
token if token.declaration_keyword().is_some() =>
|
||||
(declaration.map(BodyItem::VariableDeclaration), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||
token if token.value == "import" && matches!(token.token_type, TokenType::Keyword) =>
|
||||
@ -2058,6 +2059,52 @@ fn declaration(i: &mut TokenSlice) -> PResult<BoxNode<VariableDeclaration>> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn ty_decl(i: &mut TokenSlice) -> PResult<BoxNode<TypeDeclaration>> {
|
||||
let (visibility, visibility_token) = opt(terminated(item_visibility, whitespace))
|
||||
.parse_next(i)?
|
||||
.map_or((ItemVisibility::Default, None), |pair| (pair.0, Some(pair.1)));
|
||||
|
||||
let decl_token = ty(i)?;
|
||||
let start = visibility_token.map(|t| t.start).unwrap_or_else(|| decl_token.start);
|
||||
whitespace(i)?;
|
||||
|
||||
let name = identifier(i)?;
|
||||
let mut end = name.end;
|
||||
|
||||
let args = if peek(open_paren).parse_next(i).is_ok() {
|
||||
ignore_whitespace(i);
|
||||
open_paren(i)?;
|
||||
ignore_whitespace(i);
|
||||
let args: Vec<_> = separated(0.., identifier, comma_sep).parse_next(i)?;
|
||||
ignore_trailing_comma(i);
|
||||
ignore_whitespace(i);
|
||||
end = close_paren(i)?.end;
|
||||
Some(args)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let result = Box::new(Node {
|
||||
start,
|
||||
end,
|
||||
module_id: name.module_id,
|
||||
outer_attrs: Vec::new(),
|
||||
inner: TypeDeclaration {
|
||||
name,
|
||||
args,
|
||||
visibility,
|
||||
digest: None,
|
||||
},
|
||||
});
|
||||
|
||||
ParseContext::warn(CompilationError::err(
|
||||
result.as_source_range(),
|
||||
"Type declarations are experimental, likely to change, and may or may not do anything useful.",
|
||||
));
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl TryFrom<Token> for Node<Identifier> {
|
||||
type Error = CompilationError;
|
||||
|
||||
@ -2109,29 +2156,6 @@ fn nameable_identifier(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn sketch_keyword(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
|
||||
any.try_map(|token: Token| {
|
||||
if token.token_type == TokenType::Type && token.value == "sketch" {
|
||||
Ok(Node::new(
|
||||
Identifier {
|
||||
name: token.value,
|
||||
digest: None,
|
||||
},
|
||||
token.start,
|
||||
token.end,
|
||||
token.module_id,
|
||||
))
|
||||
} else {
|
||||
Err(CompilationError::fatal(
|
||||
token.as_source_range(),
|
||||
format!("Expected 'sketch' keyword, but found {}", token.value.as_str()),
|
||||
))
|
||||
}
|
||||
})
|
||||
.context(expected("the 'sketch' keyword"))
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
impl TryFrom<Token> for Node<TagDeclarator> {
|
||||
type Error = CompilationError;
|
||||
|
||||
@ -2467,11 +2491,19 @@ fn at_sign(i: &mut TokenSlice) -> PResult<Token> {
|
||||
}
|
||||
|
||||
fn fun(i: &mut TokenSlice) -> PResult<Token> {
|
||||
keyword(i, "fn")
|
||||
}
|
||||
|
||||
fn ty(i: &mut TokenSlice) -> PResult<Token> {
|
||||
keyword(i, "type")
|
||||
}
|
||||
|
||||
fn keyword(i: &mut TokenSlice, expected: &str) -> PResult<Token> {
|
||||
any.try_map(|token: Token| match token.token_type {
|
||||
TokenType::Keyword if token.value == "fn" => Ok(token),
|
||||
TokenType::Keyword if token.value == expected => Ok(token),
|
||||
_ => Err(CompilationError::fatal(
|
||||
token.as_source_range(),
|
||||
format!("expected 'fn', found {}", token.value.as_str(),),
|
||||
format!("expected '{expected}', found {}", token.value.as_str(),),
|
||||
)),
|
||||
})
|
||||
.parse_next(i)
|
||||
@ -2520,43 +2552,33 @@ fn argument_type(i: &mut TokenSlice) -> PResult<Node<Type>> {
|
||||
))
|
||||
}),
|
||||
// Array types
|
||||
(
|
||||
one_of(TokenType::Type),
|
||||
opt(delimited(open_paren, uom_for_type, close_paren)),
|
||||
open_bracket,
|
||||
close_bracket,
|
||||
)
|
||||
.map(|(token, uom, _, _)| {
|
||||
PrimitiveType::from_str(&token.value, uom)
|
||||
.map(|t| Node::new(Type::Array(t), token.start, token.end, token.module_id))
|
||||
.ok_or_else(|| {
|
||||
CompilationError::fatal(token.as_source_range(), format!("Invalid type: {}", token.value))
|
||||
})
|
||||
}),
|
||||
(primitive_type, open_bracket, close_bracket).map(|(t, _, _)| Ok(t.map(Type::Array))),
|
||||
// Primitive types
|
||||
(
|
||||
one_of(TokenType::Type),
|
||||
opt(delimited(open_paren, uom_for_type, close_paren)),
|
||||
)
|
||||
.map(|(token, suffix)| {
|
||||
if suffix.is_some() {
|
||||
ParseContext::warn(CompilationError::err(
|
||||
(&token).into(),
|
||||
"Unit of Measure types are experimental and currently do nothing.",
|
||||
));
|
||||
}
|
||||
PrimitiveType::from_str(&token.value, suffix)
|
||||
.map(|t| Node::new(Type::Primitive(t), token.start, token.end, token.module_id))
|
||||
.ok_or_else(|| {
|
||||
CompilationError::fatal(token.as_source_range(), format!("Invalid type: {}", token.value))
|
||||
})
|
||||
}),
|
||||
primitive_type.map(|t| Ok(t.map(Type::Primitive))),
|
||||
))
|
||||
.parse_next(i)?
|
||||
.map_err(|e: CompilationError| ErrMode::Backtrack(ContextError::from(e)))?;
|
||||
Ok(type_)
|
||||
}
|
||||
|
||||
fn primitive_type(i: &mut TokenSlice) -> PResult<Node<PrimitiveType>> {
|
||||
let ident = identifier(i)?;
|
||||
|
||||
let suffix = opt(delimited(open_paren, uom_for_type, close_paren)).parse_next(i)?;
|
||||
|
||||
let mut result = Node::new(PrimitiveType::Boolean, ident.start, ident.end, ident.module_id);
|
||||
result.inner = PrimitiveType::primitive_from_str(&ident.name, suffix).unwrap_or(PrimitiveType::Named(ident));
|
||||
|
||||
if suffix.is_some() {
|
||||
ParseContext::warn(CompilationError::err(
|
||||
result.as_source_range(),
|
||||
"Unit of Measure types are experimental and currently do nothing.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn uom_for_type(i: &mut TokenSlice) -> PResult<NumericSuffix> {
|
||||
any.try_map(|t: Token| t.value.parse()).parse_next(i)
|
||||
}
|
||||
@ -4528,18 +4550,6 @@ let myBox = box([0,0], -3, -16, -10)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tag_starting_with_reserved_type() {
|
||||
let some_program_string = r#"
|
||||
startSketchOn('XY')
|
||||
|> line(%, $Sketch)
|
||||
"#;
|
||||
assert_err(
|
||||
some_program_string,
|
||||
"Cannot assign a tag to a reserved keyword: Sketch",
|
||||
[41, 47],
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_parse_tag_with_reserved_in_middle_works() {
|
||||
let some_program_string = r#"
|
||||
|
@ -51,14 +51,6 @@ lazy_static! {
|
||||
set.insert("struct", TokenType::Keyword);
|
||||
set.insert("object", TokenType::Keyword);
|
||||
|
||||
set.insert("string", TokenType::Type);
|
||||
set.insert("number", TokenType::Type);
|
||||
set.insert("bool", TokenType::Type);
|
||||
set.insert("Sketch", TokenType::Type);
|
||||
set.insert("SketchSurface", TokenType::Type);
|
||||
set.insert("Solid", TokenType::Type);
|
||||
set.insert("Plane", TokenType::Type);
|
||||
|
||||
set
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user