Compare commits
4 Commits
list
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
078ffa02b0 | |||
d461b09a4d | |||
9c42c39ba9 | |||
aa3f40e22c |
2
.github/workflows/publish-apps-release.yml
vendored
2
.github/workflows/publish-apps-release.yml
vendored
@ -123,7 +123,7 @@ jobs:
|
|||||||
path: out
|
path: out
|
||||||
glob: '*'
|
glob: '*'
|
||||||
parent: false
|
parent: false
|
||||||
destination: 'dl.kittycad.io/releases/modeling-app/test/new-workflow'
|
destination: 'dl.kittycad.io/releases/modeling-app'
|
||||||
|
|
||||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||||
run: |
|
run: |
|
||||||
|
@ -197,24 +197,17 @@ pub struct Environment {
|
|||||||
parent: Option<EnvironmentRef>,
|
parent: Option<EnvironmentRef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NO_META: Vec<Metadata> = Vec::new();
|
||||||
|
|
||||||
impl Environment {
|
impl Environment {
|
||||||
pub fn root() -> Self {
|
pub fn root() -> Self {
|
||||||
Self {
|
Self {
|
||||||
// Prelude
|
// Prelude
|
||||||
bindings: HashMap::from([
|
bindings: HashMap::from([
|
||||||
("ZERO".to_string(), KclValue::from_number(0.0, Default::default())),
|
("ZERO".to_string(), KclValue::from_number(0.0, NO_META)),
|
||||||
(
|
("QUARTER_TURN".to_string(), KclValue::from_number(90.0, NO_META)),
|
||||||
"QUARTER_TURN".to_string(),
|
("HALF_TURN".to_string(), KclValue::from_number(180.0, NO_META)),
|
||||||
KclValue::from_number(90.0, Default::default()),
|
("THREE_QUARTER_TURN".to_string(), KclValue::from_number(270.0, NO_META)),
|
||||||
),
|
|
||||||
(
|
|
||||||
"HALF_TURN".to_string(),
|
|
||||||
KclValue::from_number(180.0, Default::default()),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"THREE_QUARTER_TURN".to_string(),
|
|
||||||
KclValue::from_number(270.0, Default::default()),
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
parent: None,
|
parent: None,
|
||||||
}
|
}
|
||||||
|
@ -86,83 +86,6 @@ pub enum KclValue {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KclValue {
|
|
||||||
pub(crate) fn metadata(&self) -> Vec<Metadata> {
|
|
||||||
match self {
|
|
||||||
KclValue::Uuid { value: _, meta } => meta.clone(),
|
|
||||||
KclValue::Bool { value: _, meta } => meta.clone(),
|
|
||||||
KclValue::Number { value: _, meta } => meta.clone(),
|
|
||||||
KclValue::Int { value: _, meta } => meta.clone(),
|
|
||||||
KclValue::String { value: _, meta } => meta.clone(),
|
|
||||||
KclValue::Array { value: _, meta } => meta.clone(),
|
|
||||||
KclValue::Object { value: _, meta } => meta.clone(),
|
|
||||||
KclValue::TagIdentifier(x) => x.meta.clone(),
|
|
||||||
KclValue::TagDeclarator(x) => vec![x.metadata()],
|
|
||||||
KclValue::Plane(x) => x.meta.clone(),
|
|
||||||
KclValue::Face(x) => x.meta.clone(),
|
|
||||||
KclValue::Sketch { value } => value.meta.clone(),
|
|
||||||
KclValue::Sketches { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
|
||||||
KclValue::Solid(x) => x.meta.clone(),
|
|
||||||
KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
|
||||||
KclValue::ImportedGeometry(x) => x.meta.clone(),
|
|
||||||
KclValue::Function { meta, .. } => meta.clone(),
|
|
||||||
KclValue::KclNone { meta, .. } => meta.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_solid_set(&self) -> Result<SolidSet> {
|
|
||||||
match self {
|
|
||||||
KclValue::Solid(e) => Ok(SolidSet::Solid(e.clone())),
|
|
||||||
KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())),
|
|
||||||
KclValue::Array { value, .. } => {
|
|
||||||
let solids: Vec<_> = value
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, v)| {
|
|
||||||
v.as_solid().map(|v| v.to_owned()).map(Box::new).ok_or_else(|| {
|
|
||||||
anyhow::anyhow!(
|
|
||||||
"expected this array to only contain solids, but element {i} was actually {}",
|
|
||||||
v.human_friendly_type()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
Ok(SolidSet::Solids(solids))
|
|
||||||
}
|
|
||||||
_ => anyhow::bail!("Not a solid or solids: {:?}", self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Human readable type name used in error messages. Should not be relied
|
|
||||||
/// on for program logic.
|
|
||||||
pub(crate) fn human_friendly_type(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
KclValue::Uuid { .. } => "Unique ID (uuid)",
|
|
||||||
KclValue::TagDeclarator(_) => "TagDeclarator",
|
|
||||||
KclValue::TagIdentifier(_) => "TagIdentifier",
|
|
||||||
KclValue::Solid(_) => "Solid",
|
|
||||||
KclValue::Solids { .. } => "Solids",
|
|
||||||
KclValue::Sketch { .. } => "Sketch",
|
|
||||||
KclValue::Sketches { .. } => "Sketches",
|
|
||||||
KclValue::ImportedGeometry(_) => "ImportedGeometry",
|
|
||||||
KclValue::Function { .. } => "Function",
|
|
||||||
KclValue::Plane(_) => "Plane",
|
|
||||||
KclValue::Face(_) => "Face",
|
|
||||||
KclValue::Bool { .. } => "boolean (true/false value)",
|
|
||||||
KclValue::Number { .. } => "number",
|
|
||||||
KclValue::Int { .. } => "integer",
|
|
||||||
KclValue::String { .. } => "string (text)",
|
|
||||||
KclValue::Array { .. } => "array (list)",
|
|
||||||
KclValue::Object { .. } => "object",
|
|
||||||
KclValue::KclNone { .. } => "None",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_function(&self) -> bool {
|
|
||||||
matches!(self, KclValue::Function { .. })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SketchSet> for KclValue {
|
impl From<SketchSet> for KclValue {
|
||||||
fn from(sg: SketchSet) -> Self {
|
fn from(sg: SketchSet) -> Self {
|
||||||
match sg {
|
match sg {
|
||||||
@ -251,8 +174,82 @@ impl From<&KclValue> for Vec<SourceRange> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl KclValue {
|
impl KclValue {
|
||||||
|
pub(crate) fn metadata(&self) -> Vec<Metadata> {
|
||||||
|
match self {
|
||||||
|
KclValue::Uuid { value: _, meta } => meta.clone(),
|
||||||
|
KclValue::Bool { value: _, meta } => meta.clone(),
|
||||||
|
KclValue::Number { value: _, meta } => meta.clone(),
|
||||||
|
KclValue::Int { value: _, meta } => meta.clone(),
|
||||||
|
KclValue::String { value: _, meta } => meta.clone(),
|
||||||
|
KclValue::Array { value: _, meta } => meta.clone(),
|
||||||
|
KclValue::Object { value: _, meta } => meta.clone(),
|
||||||
|
KclValue::TagIdentifier(x) => x.meta.clone(),
|
||||||
|
KclValue::TagDeclarator(x) => vec![x.metadata()],
|
||||||
|
KclValue::Plane(x) => x.meta.clone(),
|
||||||
|
KclValue::Face(x) => x.meta.clone(),
|
||||||
|
KclValue::Sketch { value } => value.meta.clone(),
|
||||||
|
KclValue::Sketches { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
||||||
|
KclValue::Solid(x) => x.meta.clone(),
|
||||||
|
KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
||||||
|
KclValue::ImportedGeometry(x) => x.meta.clone(),
|
||||||
|
KclValue::Function { meta, .. } => meta.clone(),
|
||||||
|
KclValue::KclNone { meta, .. } => meta.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_solid_set(&self) -> Result<SolidSet> {
|
||||||
|
match self {
|
||||||
|
KclValue::Solid(e) => Ok(SolidSet::Solid(e.clone())),
|
||||||
|
KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())),
|
||||||
|
KclValue::Array { value, .. } => {
|
||||||
|
let solids: Vec<_> = value
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, v)| {
|
||||||
|
v.as_solid().map(|v| v.to_owned()).map(Box::new).ok_or_else(|| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"expected this array to only contain solids, but element {i} was actually {}",
|
||||||
|
v.human_friendly_type()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
Ok(SolidSet::Solids(solids))
|
||||||
|
}
|
||||||
|
_ => anyhow::bail!("Not a solid or solids: {:?}", self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Human readable type name used in error messages. Should not be relied
|
||||||
|
/// on for program logic.
|
||||||
|
pub(crate) fn human_friendly_type(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
KclValue::Uuid { .. } => "Unique ID (uuid)",
|
||||||
|
KclValue::TagDeclarator(_) => "TagDeclarator",
|
||||||
|
KclValue::TagIdentifier(_) => "TagIdentifier",
|
||||||
|
KclValue::Solid(_) => "Solid",
|
||||||
|
KclValue::Solids { .. } => "Solids",
|
||||||
|
KclValue::Sketch { .. } => "Sketch",
|
||||||
|
KclValue::Sketches { .. } => "Sketches",
|
||||||
|
KclValue::ImportedGeometry(_) => "ImportedGeometry",
|
||||||
|
KclValue::Function { .. } => "Function",
|
||||||
|
KclValue::Plane(_) => "Plane",
|
||||||
|
KclValue::Face(_) => "Face",
|
||||||
|
KclValue::Bool { .. } => "boolean (true/false value)",
|
||||||
|
KclValue::Number { .. } => "number",
|
||||||
|
KclValue::Int { .. } => "integer",
|
||||||
|
KclValue::String { .. } => "string (text)",
|
||||||
|
KclValue::Array { .. } => "array (list)",
|
||||||
|
KclValue::Object { .. } => "object",
|
||||||
|
KclValue::KclNone { .. } => "None",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_function(&self) -> bool {
|
||||||
|
matches!(self, KclValue::Function { .. })
|
||||||
|
}
|
||||||
/// Put the number into a KCL value.
|
/// Put the number into a KCL value.
|
||||||
pub fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
|
pub const fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
|
||||||
Self::Number { value: f, meta }
|
Self::Number { value: f, meta }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ use crate::{
|
|||||||
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang, TagDeclarator,
|
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang, TagDeclarator,
|
||||||
UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||||
},
|
},
|
||||||
|
docs::StdLibFn,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::SourceRange,
|
executor::SourceRange,
|
||||||
parser::{
|
parser::{
|
||||||
@ -2091,18 +2092,19 @@ fn binding_name(i: TokenSlice) -> PResult<Node<Identifier>> {
|
|||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_call(i: TokenSlice) -> PResult<Node<CallExpression>> {
|
fn typecheck_all(std_fn: Box<dyn StdLibFn>, args: &[Expr]) -> PResult<()> {
|
||||||
let fn_name = identifier(i)?;
|
|
||||||
opt(whitespace).parse_next(i)?;
|
|
||||||
let _ = terminated(open_paren, opt(whitespace)).parse_next(i)?;
|
|
||||||
let args = arguments(i)?;
|
|
||||||
if let Some(std_fn) = crate::std::get_stdlib_fn(&fn_name.name) {
|
|
||||||
// Type check the arguments.
|
// Type check the arguments.
|
||||||
for (i, spec_arg) in std_fn.args(false).iter().enumerate() {
|
for (i, spec_arg) in std_fn.args(false).iter().enumerate() {
|
||||||
let Some(arg) = &args.get(i) else {
|
let Some(arg) = &args.get(i) else {
|
||||||
// The executor checks the number of arguments, so we don't need to check it here.
|
// The executor checks the number of arguments, so we don't need to check it here.
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
typecheck(spec_arg, arg)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typecheck(spec_arg: &crate::docs::StdLibFnArg, arg: &&Expr) -> PResult<()> {
|
||||||
match spec_arg.type_.as_ref() {
|
match spec_arg.type_.as_ref() {
|
||||||
"TagNode" => match &arg {
|
"TagNode" => match &arg {
|
||||||
Expr::Identifier(_) => {
|
Expr::Identifier(_) => {
|
||||||
@ -2139,7 +2141,16 @@ fn fn_call(i: TokenSlice) -> PResult<Node<CallExpression>> {
|
|||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_call(i: TokenSlice) -> PResult<Node<CallExpression>> {
|
||||||
|
let fn_name = identifier(i)?;
|
||||||
|
opt(whitespace).parse_next(i)?;
|
||||||
|
let _ = terminated(open_paren, opt(whitespace)).parse_next(i)?;
|
||||||
|
let args = arguments(i)?;
|
||||||
|
if let Some(std_fn) = crate::std::get_stdlib_fn(&fn_name.name) {
|
||||||
|
typecheck_all(std_fn, &args)?;
|
||||||
}
|
}
|
||||||
let end = preceded(opt(whitespace), close_paren).parse_next(i)?.end;
|
let end = preceded(opt(whitespace), close_paren).parse_next(i)?.end;
|
||||||
|
|
||||||
|
@ -67,6 +67,8 @@ pub enum TokenType {
|
|||||||
Unknown,
|
Unknown,
|
||||||
/// The ? symbol, used for optional values.
|
/// The ? symbol, used for optional values.
|
||||||
QuestionMark,
|
QuestionMark,
|
||||||
|
/// The @ symbol.
|
||||||
|
At,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Most KCL tokens correspond to LSP semantic tokens (but not all).
|
/// Most KCL tokens correspond to LSP semantic tokens (but not all).
|
||||||
@ -93,6 +95,7 @@ impl TryFrom<TokenType> for SemanticTokenType {
|
|||||||
| TokenType::DoublePeriod
|
| TokenType::DoublePeriod
|
||||||
| TokenType::Hash
|
| TokenType::Hash
|
||||||
| TokenType::Dollar
|
| TokenType::Dollar
|
||||||
|
| TokenType::At
|
||||||
| TokenType::Unknown => {
|
| TokenType::Unknown => {
|
||||||
anyhow::bail!("unsupported token type: {:?}", token_type)
|
anyhow::bail!("unsupported token type: {:?}", token_type)
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ pub fn token(i: &mut Input<'_>) -> PResult<Token> {
|
|||||||
'}' | ')' | ']' => brace_end,
|
'}' | ')' | ']' => brace_end,
|
||||||
',' => comma,
|
',' => comma,
|
||||||
'?' => question_mark,
|
'?' => question_mark,
|
||||||
|
'@' => at,
|
||||||
'0'..='9' => number,
|
'0'..='9' => number,
|
||||||
':' => colon,
|
':' => colon,
|
||||||
'.' => alt((number, double_period, period)),
|
'.' => alt((number, double_period, period)),
|
||||||
@ -268,6 +269,16 @@ fn question_mark(i: &mut Input<'_>) -> PResult<Token> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn at(i: &mut Input<'_>) -> PResult<Token> {
|
||||||
|
let (value, range) = '@'.with_span().parse_next(i)?;
|
||||||
|
Ok(Token::from_range(
|
||||||
|
range,
|
||||||
|
i.state.module_id,
|
||||||
|
TokenType::At,
|
||||||
|
value.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn colon(i: &mut Input<'_>) -> PResult<Token> {
|
fn colon(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(
|
||||||
|
Reference in New Issue
Block a user