KCL: End-exclusive ranges like [0..<10] (#7179)
Closes https://github.com/KittyCAD/modeling-app/issues/6843 To clarify: `[1..10]` is 1, 2, ..., 8, 9, 10 `[1..<10]` is 1, 2, ... 8, 9
This commit is contained in:
		@ -1189,6 +1189,7 @@ impl LanguageServer for Backend {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn completion(&self, params: CompletionParams) -> RpcResult<Option<CompletionResponse>> {
 | 
			
		||||
        // ADAM: This is the entrypoint.
 | 
			
		||||
        let mut completions = vec![CompletionItem {
 | 
			
		||||
            label: PIPE_OPERATOR.to_string(),
 | 
			
		||||
            label_details: None,
 | 
			
		||||
 | 
			
		||||
@ -886,7 +886,7 @@ fn array_end_start(i: &mut TokenSlice) -> PResult<Node<ArrayRangeExpression>> {
 | 
			
		||||
    ignore_whitespace(i);
 | 
			
		||||
    let start_element = expression.parse_next(i)?;
 | 
			
		||||
    ignore_whitespace(i);
 | 
			
		||||
    double_period.parse_next(i)?;
 | 
			
		||||
    let end_inclusive = alt((end_inclusive_range.map(|_| true), end_exclusive_range.map(|_| false))).parse_next(i)?;
 | 
			
		||||
    ignore_whitespace(i);
 | 
			
		||||
    let end_element = expression.parse_next(i)?;
 | 
			
		||||
    ignore_whitespace(i);
 | 
			
		||||
@ -895,7 +895,7 @@ fn array_end_start(i: &mut TokenSlice) -> PResult<Node<ArrayRangeExpression>> {
 | 
			
		||||
        ArrayRangeExpression {
 | 
			
		||||
            start_element,
 | 
			
		||||
            end_element,
 | 
			
		||||
            end_inclusive: true,
 | 
			
		||||
            end_inclusive,
 | 
			
		||||
            digest: None,
 | 
			
		||||
        },
 | 
			
		||||
        start,
 | 
			
		||||
@ -2705,7 +2705,7 @@ fn period(i: &mut TokenSlice) -> PResult<()> {
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn double_period(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
fn end_inclusive_range(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
    any.try_map(|token: Token| {
 | 
			
		||||
        if matches!(token.token_type, TokenType::DoublePeriod) {
 | 
			
		||||
            Ok(token)
 | 
			
		||||
@ -2724,6 +2724,21 @@ fn double_period(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
    .parse_next(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn end_exclusive_range(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
    any.try_map(|token: Token| {
 | 
			
		||||
        if matches!(token.token_type, TokenType::DoublePeriodLessThan) {
 | 
			
		||||
            Ok(token)
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(CompilationError::fatal(
 | 
			
		||||
                token.as_source_range(),
 | 
			
		||||
                format!("expected a '..<' but found {}", token.value.as_str()),
 | 
			
		||||
            ))
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
    .context(expected("the ..< operator, used for array ranges like [0..<10]"))
 | 
			
		||||
    .parse_next(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn colon(i: &mut TokenSlice) -> PResult<Token> {
 | 
			
		||||
    TokenType::Colon.parse_from(i)
 | 
			
		||||
}
 | 
			
		||||
@ -5344,7 +5359,6 @@ mod snapshot_tests {
 | 
			
		||||
    );
 | 
			
		||||
    snapshot_test!(aa, r#"sg = -scale"#);
 | 
			
		||||
    snapshot_test!(ab, "line(endAbsolute = [0, -1])");
 | 
			
		||||
    snapshot_test!(ac, "myArray = [0..10]");
 | 
			
		||||
    snapshot_test!(
 | 
			
		||||
        ad,
 | 
			
		||||
        r#"
 | 
			
		||||
@ -5485,6 +5499,11 @@ my14 = 4 ^ 2 - 3 ^ 2 * 2
 | 
			
		||||
           )"#
 | 
			
		||||
    );
 | 
			
		||||
    snapshot_test!(kw_function_in_binary_op, r#"val = f(x = 1) + 1"#);
 | 
			
		||||
    snapshot_test!(
 | 
			
		||||
        array_ranges,
 | 
			
		||||
        r#"incl = [1..10]
 | 
			
		||||
        excl = [0..<10]"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(unused)]
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,117 @@
 | 
			
		||||
---
 | 
			
		||||
source: kcl-lib/src/parsing/parser.rs
 | 
			
		||||
expression: actual
 | 
			
		||||
---
 | 
			
		||||
{
 | 
			
		||||
  "body": [
 | 
			
		||||
    {
 | 
			
		||||
      "commentStart": 0,
 | 
			
		||||
      "declaration": {
 | 
			
		||||
        "commentStart": 0,
 | 
			
		||||
        "end": 14,
 | 
			
		||||
        "id": {
 | 
			
		||||
          "commentStart": 0,
 | 
			
		||||
          "end": 4,
 | 
			
		||||
          "name": "incl",
 | 
			
		||||
          "start": 0,
 | 
			
		||||
          "type": "Identifier"
 | 
			
		||||
        },
 | 
			
		||||
        "init": {
 | 
			
		||||
          "commentStart": 7,
 | 
			
		||||
          "end": 14,
 | 
			
		||||
          "endElement": {
 | 
			
		||||
            "commentStart": 11,
 | 
			
		||||
            "end": 13,
 | 
			
		||||
            "raw": "10",
 | 
			
		||||
            "start": 11,
 | 
			
		||||
            "type": "Literal",
 | 
			
		||||
            "type": "Literal",
 | 
			
		||||
            "value": {
 | 
			
		||||
              "value": 10.0,
 | 
			
		||||
              "suffix": "None"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "endInclusive": true,
 | 
			
		||||
          "start": 7,
 | 
			
		||||
          "startElement": {
 | 
			
		||||
            "commentStart": 8,
 | 
			
		||||
            "end": 9,
 | 
			
		||||
            "raw": "1",
 | 
			
		||||
            "start": 8,
 | 
			
		||||
            "type": "Literal",
 | 
			
		||||
            "type": "Literal",
 | 
			
		||||
            "value": {
 | 
			
		||||
              "value": 1.0,
 | 
			
		||||
              "suffix": "None"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "type": "ArrayRangeExpression",
 | 
			
		||||
          "type": "ArrayRangeExpression"
 | 
			
		||||
        },
 | 
			
		||||
        "start": 0,
 | 
			
		||||
        "type": "VariableDeclarator"
 | 
			
		||||
      },
 | 
			
		||||
      "end": 14,
 | 
			
		||||
      "kind": "const",
 | 
			
		||||
      "start": 0,
 | 
			
		||||
      "type": "VariableDeclaration",
 | 
			
		||||
      "type": "VariableDeclaration"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "commentStart": 23,
 | 
			
		||||
      "declaration": {
 | 
			
		||||
        "commentStart": 23,
 | 
			
		||||
        "end": 38,
 | 
			
		||||
        "id": {
 | 
			
		||||
          "commentStart": 23,
 | 
			
		||||
          "end": 27,
 | 
			
		||||
          "name": "excl",
 | 
			
		||||
          "start": 23,
 | 
			
		||||
          "type": "Identifier"
 | 
			
		||||
        },
 | 
			
		||||
        "init": {
 | 
			
		||||
          "commentStart": 30,
 | 
			
		||||
          "end": 38,
 | 
			
		||||
          "endElement": {
 | 
			
		||||
            "commentStart": 35,
 | 
			
		||||
            "end": 37,
 | 
			
		||||
            "raw": "10",
 | 
			
		||||
            "start": 35,
 | 
			
		||||
            "type": "Literal",
 | 
			
		||||
            "type": "Literal",
 | 
			
		||||
            "value": {
 | 
			
		||||
              "value": 10.0,
 | 
			
		||||
              "suffix": "None"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "endInclusive": false,
 | 
			
		||||
          "start": 30,
 | 
			
		||||
          "startElement": {
 | 
			
		||||
            "commentStart": 31,
 | 
			
		||||
            "end": 32,
 | 
			
		||||
            "raw": "0",
 | 
			
		||||
            "start": 31,
 | 
			
		||||
            "type": "Literal",
 | 
			
		||||
            "type": "Literal",
 | 
			
		||||
            "value": {
 | 
			
		||||
              "value": 0.0,
 | 
			
		||||
              "suffix": "None"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "type": "ArrayRangeExpression",
 | 
			
		||||
          "type": "ArrayRangeExpression"
 | 
			
		||||
        },
 | 
			
		||||
        "start": 23,
 | 
			
		||||
        "type": "VariableDeclarator"
 | 
			
		||||
      },
 | 
			
		||||
      "end": 38,
 | 
			
		||||
      "kind": "const",
 | 
			
		||||
      "start": 23,
 | 
			
		||||
      "type": "VariableDeclaration",
 | 
			
		||||
      "type": "VariableDeclaration"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "commentStart": 0,
 | 
			
		||||
  "end": 38,
 | 
			
		||||
  "start": 0
 | 
			
		||||
}
 | 
			
		||||
@ -369,6 +369,8 @@ pub enum TokenType {
 | 
			
		||||
    Period,
 | 
			
		||||
    /// A double period: `..`.
 | 
			
		||||
    DoublePeriod,
 | 
			
		||||
    /// A double period and a less than: `..<`.
 | 
			
		||||
    DoublePeriodLessThan,
 | 
			
		||||
    /// A line comment.
 | 
			
		||||
    LineComment,
 | 
			
		||||
    /// A block comment.
 | 
			
		||||
@ -410,6 +412,7 @@ impl TryFrom<TokenType> for SemanticTokenType {
 | 
			
		||||
            | TokenType::DoubleColon
 | 
			
		||||
            | TokenType::Period
 | 
			
		||||
            | TokenType::DoublePeriod
 | 
			
		||||
            | TokenType::DoublePeriodLessThan
 | 
			
		||||
            | TokenType::Hash
 | 
			
		||||
            | TokenType::Dollar
 | 
			
		||||
            | TokenType::At
 | 
			
		||||
 | 
			
		||||
@ -87,7 +87,7 @@ pub(super) fn token(i: &mut Input<'_>) -> PResult<Token> {
 | 
			
		||||
        '0'..='9' => number,
 | 
			
		||||
        ';' => semi_colon,
 | 
			
		||||
        ':' => alt((double_colon, colon)),
 | 
			
		||||
        '.' => alt((number, double_period, period)),
 | 
			
		||||
        '.' => alt((number, double_period_less_than, double_period, period)),
 | 
			
		||||
        '#' => hash,
 | 
			
		||||
        '$' => dollar,
 | 
			
		||||
        '!' => alt((operator, bang)),
 | 
			
		||||
@ -320,6 +320,16 @@ fn double_period(i: &mut Input<'_>) -> PResult<Token> {
 | 
			
		||||
    ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn double_period_less_than(i: &mut Input<'_>) -> PResult<Token> {
 | 
			
		||||
    let (value, range) = "..<".with_span().parse_next(i)?;
 | 
			
		||||
    Ok(Token::from_range(
 | 
			
		||||
        range,
 | 
			
		||||
        i.state.module_id,
 | 
			
		||||
        TokenType::DoublePeriodLessThan,
 | 
			
		||||
        value.to_string(),
 | 
			
		||||
    ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Zero or more of either:
 | 
			
		||||
/// 1. Any character except " or \
 | 
			
		||||
/// 2. Any character preceded by \
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user