2021-12-21 20:50:18 +00:00
use std ::fmt ::Display ;
2021-11-19 02:51:42 +00:00
use serde ::{ Deserialize , Serialize } ;
2022-03-10 07:49:02 +00:00
use crate ::{ DeclId , Type } ;
2021-09-02 01:29:43 +00:00
/// The syntactic shapes that values must match to be passed into a command. You can think of this as the type-checking that occurs when you call a function.
2021-11-19 02:51:42 +00:00
#[ derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize) ]
2021-09-02 01:29:43 +00:00
pub enum SyntaxShape {
/// Any syntactic form is allowed
Any ,
2022-12-13 16:46:22 +00:00
/// A binary literal
Binary ,
/// A block is allowed, eg `{start this thing}`
Block ,
/// A boolean value, eg `true` or `false`
Boolean ,
2021-09-02 01:29:43 +00:00
/// A dotted path to navigate the table
2021-09-06 22:02:24 +00:00
CellPath ,
2021-09-02 01:29:43 +00:00
2022-12-13 16:46:22 +00:00
/// A closure is allowed, eg `{|| start this thing}`
Closure ( Option < Vec < SyntaxShape > > ) ,
2021-09-02 01:29:43 +00:00
2022-12-13 16:46:22 +00:00
/// A custom shape with custom completion logic
Custom ( Box < SyntaxShape > , DeclId ) ,
2021-09-02 01:29:43 +00:00
2022-12-13 16:46:22 +00:00
/// A datetime value, eg `2022-02-02` or `2019-10-12T07:20:50.52+00:00`
DateTime ,
2021-09-02 01:29:43 +00:00
2023-01-25 12:43:22 +00:00
/// A decimal value, eg `1.0`
Decimal ,
2022-12-13 16:46:22 +00:00
/// A directory is allowed
Directory ,
/// A duration value is allowed, eg `19day`
Duration ,
/// An error value
Error ,
/// A general expression, eg `1 + 2` or `foo --bar`
Expression ,
2021-09-02 01:29:43 +00:00
/// A filepath is allowed
2021-10-04 19:21:31 +00:00
Filepath ,
2021-09-02 01:29:43 +00:00
2022-12-13 16:46:22 +00:00
/// A filesize value is allowed, eg `10kb`
Filesize ,
/// A dotted path to navigate the table (including variable)
FullCellPath ,
2022-04-22 20:18:51 +00:00
2021-09-02 01:29:43 +00:00
/// A glob pattern is allowed, eg `foo*`
GlobPattern ,
2022-12-13 16:46:22 +00:00
/// Only an integer value is allowed
Int ,
2021-09-26 18:39:19 +00:00
/// A module path pattern used for imports
ImportPattern ,
2022-12-13 16:46:22 +00:00
/// A specific match to a word or symbol
Keyword ( Vec < u8 > , Box < SyntaxShape > ) ,
2021-09-02 01:29:43 +00:00
2022-12-04 19:20:47 +00:00
/// A list is allowed, eg `[first second]`
2021-09-02 01:29:43 +00:00
List ( Box < SyntaxShape > ) ,
2022-12-13 16:46:22 +00:00
/// A general math expression, eg `1 + 2`
MathExpression ,
2021-09-02 01:29:43 +00:00
2023-03-24 01:52:01 +00:00
/// A block of matches, used by `match`
MatchBlock ,
/// A match pattern, eg `{a: $foo}`
MatchPattern ,
2022-12-13 16:46:22 +00:00
/// Nothing
Nothing ,
2021-09-02 01:29:43 +00:00
2022-12-13 16:46:22 +00:00
/// Only a numeric (integer or decimal) value is allowed
Number ,
/// One of a list of possible items, checked in order
OneOf ( Vec < SyntaxShape > ) ,
2022-02-24 02:02:48 +00:00
2022-12-04 19:20:47 +00:00
/// An operator, eg `+`
2021-09-02 01:29:43 +00:00
Operator ,
2022-12-13 16:46:22 +00:00
/// A range is allowed (eg, `1..3`)
Range ,
/// A record value, eg `{x: 1, y: 2}`
allow records to have type annotations (#8914)
# Description
follow up to #8529
cleaned up version of #8892
- the original syntax is okay
```nu
def okay [rec: record] {}
```
- you can now add type annotations for fields if you know
them before hand
```nu
def okay [rec: record<name: string>] {}
```
- you can specify multiple fields
```nu
def okay [person: record<name: string age: int>] {}
# an optional comma is allowed
def okay [person: record<name: string, age: int>] {}
```
- if annotations are specified, any use of the command will be type
checked against the specified type
```nu
def unwrap [result: record<ok: bool, value: any>] {}
unwrap {ok: 2, value: "value"}
# errors with
Error: nu::parser::type_mismatch
× Type mismatch.
╭─[entry #4:1:1]
1 │ unwrap {ok: 2, value: "value"}
· ───────┬─────
· ╰── expected record<ok: bool, value: any>, found record<ok: int, value: string>
╰────
```
> here the error is in the `ok` field, since `any` is coerced into any
type
> as a result `unwrap {ok: true, value: "value"}` is okay
- the key must be a string, either quoted or unquoted
```nu
def err [rec: record<{}: list>] {}
# errors with
Error:
× `record` type annotations key not string
╭─[entry #7:1:1]
1 │ def unwrap [result: record<{}: bool, value: any>] {}
· ─┬
· ╰── must be a string
╰────
```
- a key doesn't have to have a type in which case it is assumed to be
`any`
```nu
def okay [person: record<name age>] {}
def okay [person: record<name: string age>] {}
```
- however, if you put a colon, you have to specify a type
```nu
def err [person: record<name: >] {}
# errors with
Error: nu::parser::parse_mismatch
× Parse mismatch during operation.
╭─[entry #12:1:1]
1 │ def unwrap [res: record<name: >] { $res }
· ┬
· ╰── expected type after colon
╰────
```
# User-Facing Changes
**[BREAKING CHANGES]**
- this change adds a field to `SyntaxShape::Record` so any plugins that
used it will have to update and include the field. though if you are
unsure of the type the record expects, `SyntaxShape::Record(vec![])`
will suffice
2023-04-26 13:16:55 +00:00
Record ( Vec < ( String , SyntaxShape ) > ) ,
2022-12-13 16:46:22 +00:00
2021-09-02 01:29:43 +00:00
/// A math expression which expands shorthand forms on the lefthand side, eg `foo > 1`
/// The shorthand allows us to more easily reach columns inside of the row being passed in
RowCondition ,
/// A signature for a definition, `[x:int, --foo]`
Signature ,
2022-12-13 16:46:22 +00:00
/// Strings and string-like bare words are allowed
String ,
2022-06-30 01:01:34 +00:00
2022-12-13 16:46:22 +00:00
/// A table is allowed, eg `[[first, second]; [1, 2]]`
2023-07-07 09:06:09 +00:00
Table ( Vec < ( String , SyntaxShape ) > ) ,
2022-11-09 21:55:05 +00:00
2022-12-13 16:46:22 +00:00
/// A variable name, eg `$foo`
Variable ,
2022-12-07 19:58:54 +00:00
2022-12-13 16:46:22 +00:00
/// A variable with optional type, `x` or `x: int`
VarWithOptType ,
2021-09-02 01:29:43 +00:00
}
impl SyntaxShape {
pub fn to_type ( & self ) -> Type {
2023-07-07 09:06:09 +00:00
let mk_ty = | tys : & [ ( String , SyntaxShape ) ] | {
tys . iter ( )
. map ( | ( key , val ) | ( key . clone ( ) , val . to_type ( ) ) )
. collect ( )
} ;
2021-09-02 01:29:43 +00:00
match self {
2022-04-07 04:34:09 +00:00
SyntaxShape ::Any = > Type ::Any ,
2022-11-10 08:21:49 +00:00
SyntaxShape ::Block = > Type ::Block ,
SyntaxShape ::Closure ( _ ) = > Type ::Closure ,
2022-02-28 23:31:53 +00:00
SyntaxShape ::Binary = > Type ::Binary ,
2022-04-07 04:34:09 +00:00
SyntaxShape ::CellPath = > Type ::Any ,
2021-09-14 04:59:46 +00:00
SyntaxShape ::Custom ( custom , _ ) = > custom . to_type ( ) ,
2022-02-24 02:02:48 +00:00
SyntaxShape ::DateTime = > Type ::Date ,
2021-09-02 01:29:43 +00:00
SyntaxShape ::Duration = > Type ::Duration ,
2022-04-07 04:34:09 +00:00
SyntaxShape ::Expression = > Type ::Any ,
2021-10-04 19:21:31 +00:00
SyntaxShape ::Filepath = > Type ::String ,
2022-04-22 20:18:51 +00:00
SyntaxShape ::Directory = > Type ::String ,
2023-01-25 12:43:22 +00:00
SyntaxShape ::Decimal = > Type ::Float ,
2021-09-02 01:29:43 +00:00
SyntaxShape ::Filesize = > Type ::Filesize ,
2022-04-07 04:34:09 +00:00
SyntaxShape ::FullCellPath = > Type ::Any ,
2021-09-02 01:29:43 +00:00
SyntaxShape ::GlobPattern = > Type ::String ,
2022-06-30 01:01:34 +00:00
SyntaxShape ::Error = > Type ::Error ,
2022-04-07 04:34:09 +00:00
SyntaxShape ::ImportPattern = > Type ::Any ,
2021-09-02 01:29:43 +00:00
SyntaxShape ::Int = > Type ::Int ,
SyntaxShape ::List ( x ) = > {
let contents = x . to_type ( ) ;
Type ::List ( Box ::new ( contents ) )
}
SyntaxShape ::Keyword ( _ , expr ) = > expr . to_type ( ) ,
2023-03-24 01:52:01 +00:00
SyntaxShape ::MatchBlock = > Type ::Any ,
SyntaxShape ::MatchPattern = > Type ::Any ,
2022-04-07 04:34:09 +00:00
SyntaxShape ::MathExpression = > Type ::Any ,
2022-12-13 16:46:22 +00:00
SyntaxShape ::Nothing = > Type ::Any ,
2021-09-02 01:29:43 +00:00
SyntaxShape ::Number = > Type ::Number ,
2022-12-07 19:58:54 +00:00
SyntaxShape ::OneOf ( _ ) = > Type ::Any ,
2022-04-07 04:34:09 +00:00
SyntaxShape ::Operator = > Type ::Any ,
SyntaxShape ::Range = > Type ::Any ,
2023-07-07 09:06:09 +00:00
SyntaxShape ::Record ( entries ) = > Type ::Record ( mk_ty ( entries ) ) ,
2021-09-02 01:29:43 +00:00
SyntaxShape ::RowCondition = > Type ::Bool ,
2021-10-12 04:49:17 +00:00
SyntaxShape ::Boolean = > Type ::Bool ,
2021-12-27 19:13:52 +00:00
SyntaxShape ::Signature = > Type ::Signature ,
2021-09-02 01:29:43 +00:00
SyntaxShape ::String = > Type ::String ,
2023-07-07 09:06:09 +00:00
SyntaxShape ::Table ( columns ) = > Type ::Table ( mk_ty ( columns ) ) ,
2022-04-07 04:34:09 +00:00
SyntaxShape ::VarWithOptType = > Type ::Any ,
SyntaxShape ::Variable = > Type ::Any ,
2021-06-29 14:27:16 +00:00
}
}
}
2021-12-21 20:50:18 +00:00
impl Display for SyntaxShape {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
2023-07-07 09:06:09 +00:00
let mk_fmt = | tys : & [ ( String , SyntaxShape ) ] | -> String {
tys . iter ( )
. map ( | ( x , y ) | format! ( " {x} : {y} " ) )
. collect ::< Vec < String > > ( )
. join ( " , " )
} ;
2021-12-21 20:50:18 +00:00
match self {
SyntaxShape ::Keyword ( kw , shape ) = > {
write! ( f , " \" {} \" {} " , String ::from_utf8_lossy ( kw ) , shape )
}
SyntaxShape ::Any = > write! ( f , " any " ) ,
SyntaxShape ::String = > write! ( f , " string " ) ,
SyntaxShape ::CellPath = > write! ( f , " cellpath " ) ,
SyntaxShape ::FullCellPath = > write! ( f , " cellpath " ) ,
SyntaxShape ::Number = > write! ( f , " number " ) ,
SyntaxShape ::Range = > write! ( f , " range " ) ,
SyntaxShape ::Int = > write! ( f , " int " ) ,
2023-01-25 12:43:22 +00:00
SyntaxShape ::Decimal = > write! ( f , " decimal " ) ,
2021-12-21 20:50:18 +00:00
SyntaxShape ::Filepath = > write! ( f , " path " ) ,
2022-04-22 20:18:51 +00:00
SyntaxShape ::Directory = > write! ( f , " directory " ) ,
2021-12-21 20:50:18 +00:00
SyntaxShape ::GlobPattern = > write! ( f , " glob " ) ,
SyntaxShape ::ImportPattern = > write! ( f , " import " ) ,
2022-11-10 08:21:49 +00:00
SyntaxShape ::Block = > write! ( f , " block " ) ,
2022-11-26 07:16:39 +00:00
SyntaxShape ::Closure ( args ) = > {
if let Some ( args ) = args {
let arg_vec : Vec < _ > = args . iter ( ) . map ( | x | x . to_string ( ) ) . collect ( ) ;
let arg_string = arg_vec . join ( " , " ) ;
2023-01-30 01:37:54 +00:00
write! ( f , " closure({arg_string}) " )
2022-11-26 07:16:39 +00:00
} else {
write! ( f , " closure() " )
}
}
2022-02-28 23:31:53 +00:00
SyntaxShape ::Binary = > write! ( f , " binary " ) ,
2023-01-30 01:37:54 +00:00
SyntaxShape ::List ( x ) = > write! ( f , " list<{x}> " ) ,
2023-07-07 09:06:09 +00:00
SyntaxShape ::Table ( columns ) = > {
if columns . is_empty ( ) {
write! ( f , " table " )
} else {
write! ( f , " table<{}> " , mk_fmt ( columns ) )
}
}
allow records to have type annotations (#8914)
# Description
follow up to #8529
cleaned up version of #8892
- the original syntax is okay
```nu
def okay [rec: record] {}
```
- you can now add type annotations for fields if you know
them before hand
```nu
def okay [rec: record<name: string>] {}
```
- you can specify multiple fields
```nu
def okay [person: record<name: string age: int>] {}
# an optional comma is allowed
def okay [person: record<name: string, age: int>] {}
```
- if annotations are specified, any use of the command will be type
checked against the specified type
```nu
def unwrap [result: record<ok: bool, value: any>] {}
unwrap {ok: 2, value: "value"}
# errors with
Error: nu::parser::type_mismatch
× Type mismatch.
╭─[entry #4:1:1]
1 │ unwrap {ok: 2, value: "value"}
· ───────┬─────
· ╰── expected record<ok: bool, value: any>, found record<ok: int, value: string>
╰────
```
> here the error is in the `ok` field, since `any` is coerced into any
type
> as a result `unwrap {ok: true, value: "value"}` is okay
- the key must be a string, either quoted or unquoted
```nu
def err [rec: record<{}: list>] {}
# errors with
Error:
× `record` type annotations key not string
╭─[entry #7:1:1]
1 │ def unwrap [result: record<{}: bool, value: any>] {}
· ─┬
· ╰── must be a string
╰────
```
- a key doesn't have to have a type in which case it is assumed to be
`any`
```nu
def okay [person: record<name age>] {}
def okay [person: record<name: string age>] {}
```
- however, if you put a colon, you have to specify a type
```nu
def err [person: record<name: >] {}
# errors with
Error: nu::parser::parse_mismatch
× Parse mismatch during operation.
╭─[entry #12:1:1]
1 │ def unwrap [res: record<name: >] { $res }
· ┬
· ╰── expected type after colon
╰────
```
# User-Facing Changes
**[BREAKING CHANGES]**
- this change adds a field to `SyntaxShape::Record` so any plugins that
used it will have to update and include the field. though if you are
unsure of the type the record expects, `SyntaxShape::Record(vec![])`
will suffice
2023-04-26 13:16:55 +00:00
SyntaxShape ::Record ( entries ) = > {
if entries . is_empty ( ) {
write! ( f , " record " )
} else {
2023-07-07 09:06:09 +00:00
write! ( f , " record<{}> " , mk_fmt ( entries ) )
allow records to have type annotations (#8914)
# Description
follow up to #8529
cleaned up version of #8892
- the original syntax is okay
```nu
def okay [rec: record] {}
```
- you can now add type annotations for fields if you know
them before hand
```nu
def okay [rec: record<name: string>] {}
```
- you can specify multiple fields
```nu
def okay [person: record<name: string age: int>] {}
# an optional comma is allowed
def okay [person: record<name: string, age: int>] {}
```
- if annotations are specified, any use of the command will be type
checked against the specified type
```nu
def unwrap [result: record<ok: bool, value: any>] {}
unwrap {ok: 2, value: "value"}
# errors with
Error: nu::parser::type_mismatch
× Type mismatch.
╭─[entry #4:1:1]
1 │ unwrap {ok: 2, value: "value"}
· ───────┬─────
· ╰── expected record<ok: bool, value: any>, found record<ok: int, value: string>
╰────
```
> here the error is in the `ok` field, since `any` is coerced into any
type
> as a result `unwrap {ok: true, value: "value"}` is okay
- the key must be a string, either quoted or unquoted
```nu
def err [rec: record<{}: list>] {}
# errors with
Error:
× `record` type annotations key not string
╭─[entry #7:1:1]
1 │ def unwrap [result: record<{}: bool, value: any>] {}
· ─┬
· ╰── must be a string
╰────
```
- a key doesn't have to have a type in which case it is assumed to be
`any`
```nu
def okay [person: record<name age>] {}
def okay [person: record<name: string age>] {}
```
- however, if you put a colon, you have to specify a type
```nu
def err [person: record<name: >] {}
# errors with
Error: nu::parser::parse_mismatch
× Parse mismatch during operation.
╭─[entry #12:1:1]
1 │ def unwrap [res: record<name: >] { $res }
· ┬
· ╰── expected type after colon
╰────
```
# User-Facing Changes
**[BREAKING CHANGES]**
- this change adds a field to `SyntaxShape::Record` so any plugins that
used it will have to update and include the field. though if you are
unsure of the type the record expects, `SyntaxShape::Record(vec![])`
will suffice
2023-04-26 13:16:55 +00:00
}
}
2021-12-21 20:50:18 +00:00
SyntaxShape ::Filesize = > write! ( f , " filesize " ) ,
SyntaxShape ::Duration = > write! ( f , " duration " ) ,
2022-02-24 02:02:48 +00:00
SyntaxShape ::DateTime = > write! ( f , " datetime " ) ,
2021-12-21 20:50:18 +00:00
SyntaxShape ::Operator = > write! ( f , " operator " ) ,
SyntaxShape ::RowCondition = > write! ( f , " condition " ) ,
SyntaxShape ::MathExpression = > write! ( f , " variable " ) ,
SyntaxShape ::Variable = > write! ( f , " var " ) ,
SyntaxShape ::VarWithOptType = > write! ( f , " vardecl " ) ,
SyntaxShape ::Signature = > write! ( f , " signature " ) ,
2023-03-24 01:52:01 +00:00
SyntaxShape ::MatchPattern = > write! ( f , " matchpattern " ) ,
SyntaxShape ::MatchBlock = > write! ( f , " matchblock " ) ,
2021-12-21 20:50:18 +00:00
SyntaxShape ::Expression = > write! ( f , " expression " ) ,
SyntaxShape ::Boolean = > write! ( f , " bool " ) ,
2022-06-30 01:01:34 +00:00
SyntaxShape ::Error = > write! ( f , " error " ) ,
2023-01-30 01:37:54 +00:00
SyntaxShape ::Custom ( x , _ ) = > write! ( f , " custom<{x}> " ) ,
2022-12-07 19:58:54 +00:00
SyntaxShape ::OneOf ( list ) = > {
let arg_vec : Vec < _ > = list . iter ( ) . map ( | x | x . to_string ( ) ) . collect ( ) ;
let arg_string = arg_vec . join ( " , " ) ;
2023-01-30 01:37:54 +00:00
write! ( f , " one_of({arg_string}) " )
2022-12-07 19:58:54 +00:00
}
2022-11-09 21:55:05 +00:00
SyntaxShape ::Nothing = > write! ( f , " nothing " ) ,
2021-12-21 20:50:18 +00:00
}
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
}
}