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
|
||||
glob: '*'
|
||||
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
|
||||
run: |
|
||||
|
@ -197,24 +197,17 @@ pub struct Environment {
|
||||
parent: Option<EnvironmentRef>,
|
||||
}
|
||||
|
||||
const NO_META: Vec<Metadata> = Vec::new();
|
||||
|
||||
impl Environment {
|
||||
pub fn root() -> Self {
|
||||
Self {
|
||||
// Prelude
|
||||
bindings: HashMap::from([
|
||||
("ZERO".to_string(), KclValue::from_number(0.0, Default::default())),
|
||||
(
|
||||
"QUARTER_TURN".to_string(),
|
||||
KclValue::from_number(90.0, Default::default()),
|
||||
),
|
||||
(
|
||||
"HALF_TURN".to_string(),
|
||||
KclValue::from_number(180.0, Default::default()),
|
||||
),
|
||||
(
|
||||
"THREE_QUARTER_TURN".to_string(),
|
||||
KclValue::from_number(270.0, Default::default()),
|
||||
),
|
||||
("ZERO".to_string(), KclValue::from_number(0.0, NO_META)),
|
||||
("QUARTER_TURN".to_string(), KclValue::from_number(90.0, NO_META)),
|
||||
("HALF_TURN".to_string(), KclValue::from_number(180.0, NO_META)),
|
||||
("THREE_QUARTER_TURN".to_string(), KclValue::from_number(270.0, NO_META)),
|
||||
]),
|
||||
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 {
|
||||
fn from(sg: SketchSet) -> Self {
|
||||
match sg {
|
||||
@ -251,8 +174,82 @@ impl From<&KclValue> for Vec<SourceRange> {
|
||||
}
|
||||
|
||||
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.
|
||||
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 }
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ use crate::{
|
||||
ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, Shebang, TagDeclarator,
|
||||
UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
},
|
||||
docs::StdLibFn,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::SourceRange,
|
||||
parser::{
|
||||
@ -2091,18 +2092,19 @@ fn binding_name(i: TokenSlice) -> PResult<Node<Identifier>> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
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) {
|
||||
fn typecheck_all(std_fn: Box<dyn StdLibFn>, args: &[Expr]) -> PResult<()> {
|
||||
// Type check the arguments.
|
||||
for (i, spec_arg) in std_fn.args(false).iter().enumerate() {
|
||||
let Some(arg) = &args.get(i) else {
|
||||
// The executor checks the number of arguments, so we don't need to check it here.
|
||||
continue;
|
||||
};
|
||||
typecheck(spec_arg, arg)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn typecheck(spec_arg: &crate::docs::StdLibFnArg, arg: &&Expr) -> PResult<()> {
|
||||
match spec_arg.type_.as_ref() {
|
||||
"TagNode" => match &arg {
|
||||
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;
|
||||
|
||||
|
@ -67,6 +67,8 @@ pub enum TokenType {
|
||||
Unknown,
|
||||
/// The ? symbol, used for optional values.
|
||||
QuestionMark,
|
||||
/// The @ symbol.
|
||||
At,
|
||||
}
|
||||
|
||||
/// Most KCL tokens correspond to LSP semantic tokens (but not all).
|
||||
@ -93,6 +95,7 @@ impl TryFrom<TokenType> for SemanticTokenType {
|
||||
| TokenType::DoublePeriod
|
||||
| TokenType::Hash
|
||||
| TokenType::Dollar
|
||||
| TokenType::At
|
||||
| TokenType::Unknown => {
|
||||
anyhow::bail!("unsupported token type: {:?}", token_type)
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ pub fn token(i: &mut Input<'_>) -> PResult<Token> {
|
||||
'}' | ')' | ']' => brace_end,
|
||||
',' => comma,
|
||||
'?' => question_mark,
|
||||
'@' => at,
|
||||
'0'..='9' => number,
|
||||
':' => colon,
|
||||
'.' => 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> {
|
||||
let (value, range) = ':'.with_span().parse_next(i)?;
|
||||
Ok(Token::from_range(
|
||||
|
Reference in New Issue
Block a user