parse union and fancy array types
Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -335,7 +335,7 @@ impl FnData {
|
|||||||
name,
|
name,
|
||||||
qual_name,
|
qual_name,
|
||||||
args: expr.params.iter().map(ArgData::from_ast).collect(),
|
args: expr.params.iter().map(ArgData::from_ast).collect(),
|
||||||
return_type: expr.return_type.as_ref().map(|t| t.recast(&Default::default(), 0)),
|
return_type: expr.return_type.as_ref().map(|t| t.to_string()),
|
||||||
properties: Properties {
|
properties: Properties {
|
||||||
exported: !var.visibility.is_default(),
|
exported: !var.visibility.is_default(),
|
||||||
deprecated: false,
|
deprecated: false,
|
||||||
@ -496,7 +496,7 @@ impl ArgData {
|
|||||||
fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self {
|
fn from_ast(arg: &crate::parsing::ast::types::Parameter) -> Self {
|
||||||
ArgData {
|
ArgData {
|
||||||
name: arg.identifier.name.clone(),
|
name: arg.identifier.name.clone(),
|
||||||
ty: arg.type_.as_ref().map(|t| t.recast(&Default::default(), 0)),
|
ty: arg.type_.as_ref().map(|t| t.to_string()),
|
||||||
// Doc comments are not yet supported on parameters.
|
// Doc comments are not yet supported on parameters.
|
||||||
docs: None,
|
docs: None,
|
||||||
kind: if arg.labeled {
|
kind: if arg.labeled {
|
||||||
|
@ -939,9 +939,14 @@ impl RuntimeType {
|
|||||||
Type::Primitive(pt) => {
|
Type::Primitive(pt) => {
|
||||||
PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Primitive)
|
PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Primitive)
|
||||||
}
|
}
|
||||||
Type::Array(pt) => {
|
Type::Array { ty, len } => {
|
||||||
PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(|t| RuntimeType::Array(t, ArrayLen::None))
|
PrimitiveType::from_parsed(ty, exec_state, source_range)?.map(|t| RuntimeType::Array(t, len))
|
||||||
}
|
}
|
||||||
|
Type::Union { tys } => tys
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| PrimitiveType::from_parsed(t.inner, exec_state, source_range))
|
||||||
|
.collect::<Result<Option<Vec<_>>, CompilationError>>()?
|
||||||
|
.map(RuntimeType::Union),
|
||||||
Type::Object { properties } => properties
|
Type::Object { properties } => properties
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
@ -1034,7 +1039,7 @@ impl fmt::Display for RuntimeType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, ts_rs::TS, JsonSchema)]
|
||||||
pub enum ArrayLen {
|
pub enum ArrayLen {
|
||||||
None,
|
None,
|
||||||
NonEmpty,
|
NonEmpty,
|
||||||
|
@ -344,8 +344,8 @@ impl Node<Type> {
|
|||||||
let range = self.as_source_range();
|
let range = self.as_source_range();
|
||||||
if range.contains(pos) {
|
if range.contains(pos) {
|
||||||
match &self.inner {
|
match &self.inner {
|
||||||
Type::Array(t) | Type::Primitive(t) => {
|
Type::Array { ty, .. } | Type::Primitive(ty) => {
|
||||||
let mut name = t.to_string();
|
let mut name = ty.to_string();
|
||||||
if name.ends_with(')') {
|
if name.ends_with(')') {
|
||||||
name.truncate(name.find('(').unwrap());
|
name.truncate(name.find('(').unwrap());
|
||||||
}
|
}
|
||||||
@ -379,7 +379,7 @@ impl FunctionExpression {
|
|||||||
if let Some(value) = self.body.get_expr_for_position(pos) {
|
if let Some(value) = self.body.get_expr_for_position(pos) {
|
||||||
let mut vars = opts.vars.clone().unwrap_or_default();
|
let mut vars = opts.vars.clone().unwrap_or_default();
|
||||||
for arg in &self.params {
|
for arg in &self.params {
|
||||||
let ty = arg.type_.as_ref().map(|ty| ty.recast(&FormatOptions::default(), 0));
|
let ty = arg.type_.as_ref().map(|ty| ty.to_string());
|
||||||
vars.insert(arg.identifier.inner.name.clone(), ty);
|
vars.insert(arg.identifier.inner.name.clone(), ty);
|
||||||
}
|
}
|
||||||
return value.get_hover_value_for_position(
|
return value.get_hover_value_for_position(
|
||||||
|
@ -1900,7 +1900,7 @@ async fn test_kcl_lsp_diagnostic_has_errors() {
|
|||||||
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
|
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
diagnostics.full_document_diagnostic_report.items[0].message,
|
diagnostics.full_document_diagnostic_report.items[0].message,
|
||||||
"lexical: found unknown token ';'"
|
"Unexpected token: ;"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected full diagnostics");
|
panic!("Expected full diagnostics");
|
||||||
|
@ -194,9 +194,21 @@ impl Type {
|
|||||||
hasher.update(b"FnArgType::Primitive");
|
hasher.update(b"FnArgType::Primitive");
|
||||||
hasher.update(prim.compute_digest())
|
hasher.update(prim.compute_digest())
|
||||||
}
|
}
|
||||||
Type::Array(prim) => {
|
Type::Array { ty, len } => {
|
||||||
hasher.update(b"FnArgType::Array");
|
hasher.update(b"FnArgType::Array");
|
||||||
hasher.update(prim.compute_digest())
|
hasher.update(ty.compute_digest());
|
||||||
|
match len {
|
||||||
|
crate::execution::kcl_value::ArrayLen::None => {}
|
||||||
|
crate::execution::kcl_value::ArrayLen::NonEmpty => hasher.update(usize::MAX.to_ne_bytes()),
|
||||||
|
crate::execution::kcl_value::ArrayLen::Known(n) => hasher.update(n.to_ne_bytes()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Union { tys } => {
|
||||||
|
hasher.update(b"FnArgType::Union");
|
||||||
|
hasher.update(tys.len().to_ne_bytes());
|
||||||
|
for t in tys.iter_mut() {
|
||||||
|
hasher.update(t.compute_digest());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Type::Object { properties } => {
|
Type::Object { properties } => {
|
||||||
hasher.update(b"FnArgType::Object");
|
hasher.update(b"FnArgType::Object");
|
||||||
|
@ -25,7 +25,7 @@ pub use crate::parsing::ast::types::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
execution::{annotations, KclValue, Metadata, TagIdentifier},
|
execution::{annotations, kcl_value::ArrayLen, KclValue, Metadata, TagIdentifier},
|
||||||
parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR},
|
parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR},
|
||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
ModuleId,
|
ModuleId,
|
||||||
@ -150,7 +150,7 @@ impl<T> Node<T> {
|
|||||||
self.start <= pos && pos <= self.end
|
self.start <= pos && pos <= self.end
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map<U>(self, f: fn(T) -> U) -> Node<U> {
|
pub fn map<U>(self, f: impl Fn(T) -> U) -> Node<U> {
|
||||||
Node {
|
Node {
|
||||||
inner: f(self.inner),
|
inner: f(self.inner),
|
||||||
start: self.start,
|
start: self.start,
|
||||||
@ -3024,7 +3024,14 @@ pub enum Type {
|
|||||||
/// A primitive type.
|
/// A primitive type.
|
||||||
Primitive(PrimitiveType),
|
Primitive(PrimitiveType),
|
||||||
// An array of a primitive type.
|
// An array of a primitive type.
|
||||||
Array(PrimitiveType),
|
Array {
|
||||||
|
ty: PrimitiveType,
|
||||||
|
len: ArrayLen,
|
||||||
|
},
|
||||||
|
// Union/enum types
|
||||||
|
Union {
|
||||||
|
tys: NodeList<PrimitiveType>,
|
||||||
|
},
|
||||||
// An object type.
|
// An object type.
|
||||||
Object {
|
Object {
|
||||||
properties: Vec<Parameter>,
|
properties: Vec<Parameter>,
|
||||||
@ -3035,7 +3042,22 @@ impl fmt::Display for Type {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Type::Primitive(primitive_type) => primitive_type.fmt(f),
|
Type::Primitive(primitive_type) => primitive_type.fmt(f),
|
||||||
Type::Array(primitive_type) => write!(f, "[{primitive_type}]"),
|
Type::Array { ty, len } => {
|
||||||
|
write!(f, "[{ty}")?;
|
||||||
|
match len {
|
||||||
|
ArrayLen::None => {}
|
||||||
|
ArrayLen::NonEmpty => write!(f, "; 1+")?,
|
||||||
|
ArrayLen::Known(n) => write!(f, "; {n}")?,
|
||||||
|
}
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
Type::Union { tys } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
tys.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
|
||||||
|
)
|
||||||
|
}
|
||||||
Type::Object { properties } => {
|
Type::Object { properties } => {
|
||||||
write!(f, "{{")?;
|
write!(f, "{{")?;
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
@ -3624,11 +3646,17 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
assert_eq!(params.len(), 3);
|
assert_eq!(params.len(), 3);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
params[0].type_.as_ref().unwrap().inner,
|
params[0].type_.as_ref().unwrap().inner,
|
||||||
Type::Array(PrimitiveType::Number(NumericSuffix::None))
|
Type::Array {
|
||||||
|
ty: PrimitiveType::Number(NumericSuffix::None),
|
||||||
|
len: ArrayLen::None
|
||||||
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
params[1].type_.as_ref().unwrap().inner,
|
params[1].type_.as_ref().unwrap().inner,
|
||||||
Type::Array(PrimitiveType::String)
|
Type::Array {
|
||||||
|
ty: PrimitiveType::String,
|
||||||
|
len: ArrayLen::None
|
||||||
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
params[2].type_.as_ref().unwrap().inner,
|
params[2].type_.as_ref().unwrap().inner,
|
||||||
@ -3656,7 +3684,10 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
assert_eq!(params.len(), 3);
|
assert_eq!(params.len(), 3);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
params[0].type_.as_ref().unwrap().inner,
|
params[0].type_.as_ref().unwrap().inner,
|
||||||
Type::Array(PrimitiveType::Number(NumericSuffix::None))
|
Type::Array {
|
||||||
|
ty: PrimitiveType::Number(NumericSuffix::None),
|
||||||
|
len: ArrayLen::None
|
||||||
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
params[1].type_.as_ref().unwrap().inner,
|
params[1].type_.as_ref().unwrap().inner,
|
||||||
@ -3692,7 +3723,15 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
56,
|
56,
|
||||||
module_id,
|
module_id,
|
||||||
),
|
),
|
||||||
type_: Some(Node::new(Type::Array(PrimitiveType::String), 59, 65, module_id)),
|
type_: Some(Node::new(
|
||||||
|
Type::Array {
|
||||||
|
ty: PrimitiveType::String,
|
||||||
|
len: ArrayLen::None
|
||||||
|
},
|
||||||
|
59,
|
||||||
|
65,
|
||||||
|
module_id
|
||||||
|
)),
|
||||||
default_value: None,
|
default_value: None,
|
||||||
labeled: true,
|
labeled: true,
|
||||||
digest: None
|
digest: None
|
||||||
@ -3773,7 +3812,15 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
34,
|
34,
|
||||||
module_id,
|
module_id,
|
||||||
),
|
),
|
||||||
type_: Some(Node::new(Type::Array(PrimitiveType::String), 37, 43, module_id)),
|
type_: Some(Node::new(
|
||||||
|
Type::Array {
|
||||||
|
ty: PrimitiveType::String,
|
||||||
|
len: ArrayLen::None
|
||||||
|
},
|
||||||
|
37,
|
||||||
|
43,
|
||||||
|
module_id
|
||||||
|
)),
|
||||||
default_value: None,
|
default_value: None,
|
||||||
labeled: true,
|
labeled: true,
|
||||||
digest: None
|
digest: None
|
||||||
|
@ -19,6 +19,7 @@ use super::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::{CompilationError, Severity, Tag},
|
errors::{CompilationError, Severity, Tag},
|
||||||
|
execution::kcl_value::ArrayLen,
|
||||||
parsing::{
|
parsing::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||||
@ -2336,21 +2337,7 @@ impl TryFrom<Token> for Node<TagDeclarator> {
|
|||||||
format!("Cannot assign a tag to a reserved keyword: {}", token.value.as_str()),
|
format!("Cannot assign a tag to a reserved keyword: {}", token.value.as_str()),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
TokenType::Bang
|
_ => Err(CompilationError::fatal(
|
||||||
| TokenType::At
|
|
||||||
| TokenType::Hash
|
|
||||||
| TokenType::Colon
|
|
||||||
| TokenType::Period
|
|
||||||
| TokenType::Operator
|
|
||||||
| TokenType::DoublePeriod
|
|
||||||
| TokenType::QuestionMark
|
|
||||||
| TokenType::BlockComment
|
|
||||||
| TokenType::Function
|
|
||||||
| TokenType::String
|
|
||||||
| TokenType::Dollar
|
|
||||||
| TokenType::Keyword
|
|
||||||
| TokenType::Unknown
|
|
||||||
| TokenType::LineComment => Err(CompilationError::fatal(
|
|
||||||
token.as_source_range(),
|
token.as_source_range(),
|
||||||
// this is `start with` because if most of these cases are in the middle, it ends
|
// this is `start with` because if most of these cases are in the middle, it ends
|
||||||
// up hitting a different error path(e.g. including a bang) or being valid(e.g. including a comment) since it will get broken up into
|
// up hitting a different error path(e.g. including a bang) or being valid(e.g. including a comment) since it will get broken up into
|
||||||
@ -2617,6 +2604,14 @@ fn colon(i: &mut TokenSlice) -> PResult<Token> {
|
|||||||
TokenType::Colon.parse_from(i)
|
TokenType::Colon.parse_from(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn semi_colon(i: &mut TokenSlice) -> PResult<Token> {
|
||||||
|
TokenType::SemiColon.parse_from(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn plus(i: &mut TokenSlice) -> PResult<Token> {
|
||||||
|
one_of((TokenType::Operator, "+")).parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
fn equals(i: &mut TokenSlice) -> PResult<Token> {
|
fn equals(i: &mut TokenSlice) -> PResult<Token> {
|
||||||
one_of((TokenType::Operator, "="))
|
one_of((TokenType::Operator, "="))
|
||||||
.context(expected("the equals operator, ="))
|
.context(expected("the equals operator, ="))
|
||||||
@ -2659,6 +2654,12 @@ fn comma_sep(i: &mut TokenSlice) -> PResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a `|`, optionally followed by some whitespace.
|
||||||
|
fn pipe_sep(i: &mut TokenSlice) -> PResult<()> {
|
||||||
|
(opt(whitespace), one_of((TokenType::Operator, "|")), opt(whitespace)).parse_next(i)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Arguments are passed into a function.
|
/// Arguments are passed into a function.
|
||||||
fn arguments(i: &mut TokenSlice) -> PResult<Vec<Expr>> {
|
fn arguments(i: &mut TokenSlice) -> PResult<Vec<Expr>> {
|
||||||
separated(0.., expression, comma_sep)
|
separated(0.., expression, comma_sep)
|
||||||
@ -2686,20 +2687,29 @@ fn argument_type(i: &mut TokenSlice) -> PResult<Node<Type>> {
|
|||||||
// Object types
|
// Object types
|
||||||
// TODO it is buggy to treat object fields like parameters since the parameters parser assumes a terminating `)`.
|
// TODO it is buggy to treat object fields like parameters since the parameters parser assumes a terminating `)`.
|
||||||
(open_brace, parameters, close_brace).map(|(open, params, close)| {
|
(open_brace, parameters, close_brace).map(|(open, params, close)| {
|
||||||
Ok(Node::new(
|
Node::new(
|
||||||
Type::Object { properties: params },
|
Type::Object { properties: params },
|
||||||
open.start,
|
open.start,
|
||||||
close.end,
|
close.end,
|
||||||
open.module_id,
|
open.module_id,
|
||||||
))
|
)
|
||||||
}),
|
}),
|
||||||
// Array types
|
// Array types
|
||||||
(open_bracket, primitive_type, close_bracket).map(|(_, t, _)| Ok(t.map(Type::Array))),
|
array_type,
|
||||||
// Primitive types
|
// Primitive or union types
|
||||||
primitive_type.map(|t| Ok(t.map(Type::Primitive))),
|
separated(1.., primitive_type, pipe_sep).map(|mut tys: Vec<_>| {
|
||||||
|
if tys.len() == 1 {
|
||||||
|
tys.pop().unwrap().map(Type::Primitive)
|
||||||
|
} else {
|
||||||
|
let start = tys[0].start;
|
||||||
|
let module_id = tys[0].module_id;
|
||||||
|
let end = tys.last().unwrap().end;
|
||||||
|
Node::new(Type::Union { tys }, start, end, module_id)
|
||||||
|
}
|
||||||
|
}),
|
||||||
))
|
))
|
||||||
.parse_next(i)?
|
.parse_next(i)?;
|
||||||
.map_err(|e: CompilationError| ErrMode::Backtrack(ContextError::from(e)))?;
|
|
||||||
Ok(type_)
|
Ok(type_)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2721,6 +2731,55 @@ fn primitive_type(i: &mut TokenSlice) -> PResult<Node<PrimitiveType>> {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn array_type(i: &mut TokenSlice) -> PResult<Node<Type>> {
|
||||||
|
fn opt_whitespace(i: &mut TokenSlice) -> PResult<()> {
|
||||||
|
ignore_whitespace(i);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
open_bracket(i)?;
|
||||||
|
let ty = primitive_type(i)?;
|
||||||
|
let len = opt((
|
||||||
|
semi_colon,
|
||||||
|
opt_whitespace,
|
||||||
|
any.try_map(|token: Token| match token.token_type {
|
||||||
|
TokenType::Number => {
|
||||||
|
let value = token.uint_value().ok_or_else(|| {
|
||||||
|
CompilationError::fatal(
|
||||||
|
token.as_source_range(),
|
||||||
|
format!("Expected unsigned integer literal, found: {}", token.value),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(value as usize)
|
||||||
|
}
|
||||||
|
_ => Err(CompilationError::fatal(token.as_source_range(), "invalid array length")),
|
||||||
|
}),
|
||||||
|
opt(plus),
|
||||||
|
))
|
||||||
|
.parse_next(i)?;
|
||||||
|
close_bracket(i)?;
|
||||||
|
|
||||||
|
let len = if let Some((tok, _, n, plus)) = len {
|
||||||
|
if plus.is_some() {
|
||||||
|
if n != 1 {
|
||||||
|
return Err(ErrMode::Cut(ContextError::from(CompilationError::fatal(
|
||||||
|
tok.as_source_range(),
|
||||||
|
"Non-empty arrays are specified using `1+`, for a fixed-size array use just an integer",
|
||||||
|
))));
|
||||||
|
} else {
|
||||||
|
ArrayLen::NonEmpty
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ArrayLen::Known(n)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ArrayLen::None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ty.map(|ty| Type::Array { ty, len }))
|
||||||
|
}
|
||||||
|
|
||||||
fn uom_for_type(i: &mut TokenSlice) -> PResult<NumericSuffix> {
|
fn uom_for_type(i: &mut TokenSlice) -> PResult<NumericSuffix> {
|
||||||
any.try_map(|t: Token| t.value.parse()).parse_next(i)
|
any.try_map(|t: Token| t.value.parse()).parse_next(i)
|
||||||
}
|
}
|
||||||
|
@ -367,6 +367,8 @@ pub enum TokenType {
|
|||||||
QuestionMark,
|
QuestionMark,
|
||||||
/// The @ symbol.
|
/// The @ symbol.
|
||||||
At,
|
At,
|
||||||
|
/// `;`
|
||||||
|
SemiColon,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Most KCL tokens correspond to LSP semantic tokens (but not all).
|
/// Most KCL tokens correspond to LSP semantic tokens (but not all).
|
||||||
@ -396,6 +398,7 @@ impl TryFrom<TokenType> for SemanticTokenType {
|
|||||||
| TokenType::Hash
|
| TokenType::Hash
|
||||||
| TokenType::Dollar
|
| TokenType::Dollar
|
||||||
| TokenType::At
|
| TokenType::At
|
||||||
|
| TokenType::SemiColon
|
||||||
| TokenType::Unknown => {
|
| TokenType::Unknown => {
|
||||||
anyhow::bail!("unsupported token type: {:?}", token_type)
|
anyhow::bail!("unsupported token type: {:?}", token_type)
|
||||||
}
|
}
|
||||||
@ -488,6 +491,18 @@ impl Token {
|
|||||||
value.parse().ok()
|
value.parse().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uint_value(&self) -> Option<u32> {
|
||||||
|
if self.token_type != TokenType::Number {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let value = &self.value;
|
||||||
|
let value = value
|
||||||
|
.split_once(|c: char| c == '_' || c.is_ascii_alphabetic())
|
||||||
|
.map(|(s, _)| s)
|
||||||
|
.unwrap_or(value);
|
||||||
|
value.parse().ok()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn numeric_suffix(&self) -> NumericSuffix {
|
pub fn numeric_suffix(&self) -> NumericSuffix {
|
||||||
if self.token_type != TokenType::Number {
|
if self.token_type != TokenType::Number {
|
||||||
return NumericSuffix::None;
|
return NumericSuffix::None;
|
||||||
|
@ -88,6 +88,7 @@ pub(super) fn token(i: &mut Input<'_>) -> PResult<Token> {
|
|||||||
'@' => at,
|
'@' => at,
|
||||||
'0'..='9' => number,
|
'0'..='9' => number,
|
||||||
':' => colon,
|
':' => colon,
|
||||||
|
';' => semi_colon,
|
||||||
'.' => alt((number, double_period, period)),
|
'.' => alt((number, double_period, period)),
|
||||||
'#' => hash,
|
'#' => hash,
|
||||||
'$' => dollar,
|
'$' => dollar,
|
||||||
@ -282,6 +283,16 @@ fn colon(i: &mut Input<'_>) -> PResult<Token> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn semi_colon(i: &mut Input<'_>) -> PResult<Token> {
|
||||||
|
let (value, range) = ';'.with_span().parse_next(i)?;
|
||||||
|
Ok(Token::from_range(
|
||||||
|
range,
|
||||||
|
i.state.module_id,
|
||||||
|
TokenType::SemiColon,
|
||||||
|
value.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn period(i: &mut Input<'_>) -> PResult<Token> {
|
fn period(i: &mut Input<'_>) -> PResult<Token> {
|
||||||
let (value, range) = '.'.with_span().parse_next(i)?;
|
let (value, range) = '.'.with_span().parse_next(i)?;
|
||||||
Ok(Token::from_range(
|
Ok(Token::from_range(
|
||||||
@ -689,7 +700,7 @@ const things = "things"
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_unrecognized_token() {
|
fn test_unrecognized_token() {
|
||||||
let module_id = ModuleId::from_usize(1);
|
let module_id = ModuleId::from_usize(1);
|
||||||
let actual = lex("12 ; 8", module_id).unwrap();
|
let actual = lex("12 ~ 8", module_id).unwrap();
|
||||||
|
|
||||||
use TokenType::*;
|
use TokenType::*;
|
||||||
assert_tokens(&[(Number, 0, 2), (Unknown, 3, 4), (Number, 5, 6)], actual.as_slice());
|
assert_tokens(&[(Number, 0, 2), (Unknown, 3, 4), (Number, 5, 6)], actual.as_slice());
|
||||||
|
@ -6,8 +6,7 @@ use crate::parsing::{
|
|||||||
CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions, FunctionExpression,
|
CallExpression, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions, FunctionExpression,
|
||||||
IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier,
|
IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier,
|
||||||
LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter,
|
LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter,
|
||||||
PipeExpression, Program, TagDeclarator, Type, TypeDeclaration, UnaryExpression, VariableDeclaration,
|
PipeExpression, Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableKind,
|
||||||
VariableKind,
|
|
||||||
},
|
},
|
||||||
token::NumericSuffix,
|
token::NumericSuffix,
|
||||||
PIPE_OPERATOR,
|
PIPE_OPERATOR,
|
||||||
@ -308,7 +307,7 @@ impl Expr {
|
|||||||
Expr::AscribedExpression(e) => {
|
Expr::AscribedExpression(e) => {
|
||||||
let mut result = e.expr.recast(options, indentation_level, ctxt);
|
let mut result = e.expr.recast(options, indentation_level, ctxt);
|
||||||
result += ": ";
|
result += ": ";
|
||||||
result += &e.ty.recast(options, indentation_level);
|
result += &e.ty.to_string();
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
Expr::None(_) => {
|
Expr::None(_) => {
|
||||||
@ -812,7 +811,7 @@ impl FunctionExpression {
|
|||||||
let tab0 = options.get_indentation(indentation_level);
|
let tab0 = options.get_indentation(indentation_level);
|
||||||
let tab1 = options.get_indentation(indentation_level + 1);
|
let tab1 = options.get_indentation(indentation_level + 1);
|
||||||
let return_type = match &self.return_type {
|
let return_type = match &self.return_type {
|
||||||
Some(rt) => format!(": {}", rt.recast(&new_options, indentation_level)),
|
Some(rt) => format!(": {}", rt.to_string()),
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
};
|
};
|
||||||
let body = self.body.recast(&new_options, indentation_level + 1);
|
let body = self.body.recast(&new_options, indentation_level + 1);
|
||||||
@ -822,14 +821,14 @@ impl FunctionExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Parameter {
|
impl Parameter {
|
||||||
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
pub fn recast(&self, _options: &FormatOptions, _indentation_level: usize) -> String {
|
||||||
let at_sign = if self.labeled { "" } else { "@" };
|
let at_sign = if self.labeled { "" } else { "@" };
|
||||||
let identifier = &self.identifier.name;
|
let identifier = &self.identifier.name;
|
||||||
let question_mark = if self.default_value.is_some() { "?" } else { "" };
|
let question_mark = if self.default_value.is_some() { "?" } else { "" };
|
||||||
let mut result = format!("{at_sign}{identifier}{question_mark}");
|
let mut result = format!("{at_sign}{identifier}{question_mark}");
|
||||||
if let Some(ty) = &self.type_ {
|
if let Some(ty) = &self.type_ {
|
||||||
result += ": ";
|
result += ": ";
|
||||||
result += &ty.recast(options, indentation_level);
|
result += &ty.to_string();
|
||||||
}
|
}
|
||||||
if let Some(DefaultParamVal::Literal(ref literal)) = self.default_value {
|
if let Some(DefaultParamVal::Literal(ref literal)) = self.default_value {
|
||||||
let lit = literal.recast();
|
let lit = literal.recast();
|
||||||
@ -840,31 +839,6 @@ impl Parameter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
|
||||||
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
|
||||||
match self {
|
|
||||||
Type::Primitive(t) => t.to_string(),
|
|
||||||
Type::Array(t) => format!("[{t}]"),
|
|
||||||
Type::Object { properties } => {
|
|
||||||
let mut result = "{".to_owned();
|
|
||||||
for p in properties {
|
|
||||||
result += " ";
|
|
||||||
result += &p.recast(options, indentation_level);
|
|
||||||
result += ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.ends_with(',') {
|
|
||||||
result.pop();
|
|
||||||
result += " ";
|
|
||||||
}
|
|
||||||
result += "}";
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collect all the kcl files in a directory, recursively.
|
/// Collect all the kcl files in a directory, recursively.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[async_recursion::async_recursion]
|
#[async_recursion::async_recursion]
|
||||||
@ -1414,6 +1388,21 @@ thing(1)
|
|||||||
assert_eq!(recasted, some_program_string);
|
assert_eq!(recasted, some_program_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_typed_consts() {
|
||||||
|
let some_program_string = r#"a = 42: number
|
||||||
|
export b = 3.2: number(ft)
|
||||||
|
c = "dsfds": A | B | C
|
||||||
|
d = [1]: [number]
|
||||||
|
e = foo: [number; 3]
|
||||||
|
f = [1, 2, 3]: [number; 1+]
|
||||||
|
"#;
|
||||||
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
|
||||||
|
let recasted = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(recasted, some_program_string);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recast_object_fn_in_array_weird_bracket() {
|
fn test_recast_object_fn_in_array_weird_bracket() {
|
||||||
let some_program_string = r#"bing = { yo = 55 }
|
let some_program_string = r#"bing = { yo = 55 }
|
||||||
|
Reference in New Issue
Block a user