use super::{CompletedMarker, Parser};
use crate::syntax_kind::SyntaxKind::*;
#[derive(Default, Clone, Copy)]
pub struct TypeOpts {
pub allow_optional: bool,
}
impl TypeOpts {
pub fn allow_optional(mut self) -> Self {
self.allow_optional = true;
self
}
}
impl Parser<'_, '_> {
pub const PRIMITIVE_TYPE_KINDS: &'static [crate::syntax_kind::SyntaxKind] = &[
KW_ADDRESS,
KW_BOOL,
KW_FIELD,
KW_GROUP,
KW_SCALAR,
KW_SIGNATURE,
KW_STRING,
KW_IDENTIFIER,
KW_DYN,
KW_I8,
KW_I16,
KW_I32,
KW_I64,
KW_I128,
KW_U8,
KW_U16,
KW_U32,
KW_U64,
KW_U128,
];
pub fn parse_type(&mut self) -> Option<CompletedMarker> {
self.parse_type_with_opts(TypeOpts::default().allow_optional())
}
pub fn parse_type_with_opts(&mut self, opts: TypeOpts) -> Option<CompletedMarker> {
let ty = self.parse_type_inner()?;
if opts.allow_optional && self.at(QUESTION) {
let m = ty.precede(self);
self.bump_any(); return Some(m.complete(self, TYPE_OPTIONAL));
}
Some(ty)
}
fn parse_type_inner(&mut self) -> Option<CompletedMarker> {
self.skip_trivia();
match self.current() {
L_PAREN => self.parse_tuple_type(),
L_BRACKET => self.parse_array_or_vector_type(),
KW_FINAL_UPPER => self.parse_future_type(),
KW_MAPPING => self.parse_mapping_type(),
KW_DYN => self.parse_dyn_record_type(),
_ if self.at_primitive_type() => self.parse_primitive_type(),
IDENT => self.parse_named_type(),
_ => None,
}
}
fn at_primitive_type(&self) -> bool {
Self::PRIMITIVE_TYPE_KINDS.contains(&self.current())
}
pub fn parse_cast_type(&mut self) -> Option<CompletedMarker> {
if self.at_primitive_type() {
self.parse_primitive_type()
} else if self.at(KW_DYN) {
self.parse_dyn_record_type()
} else {
None
}
}
fn parse_primitive_type(&mut self) -> Option<CompletedMarker> {
if !self.at_primitive_type() {
return None;
}
self.skip_trivia();
let m = self.start();
let kind = self.current();
self.bump_raw();
if kind == KW_DYN {
self.expect(KW_RECORD);
}
Some(m.complete(self, TYPE_PRIMITIVE))
}
fn parse_dyn_record_type(&mut self) -> Option<CompletedMarker> {
if !self.at(KW_DYN) {
return None;
}
self.skip_trivia();
let m = self.start();
self.bump_any(); if !self.eat(KW_RECORD) {
self.error("expected `record` after `dyn`");
}
Some(m.complete(self, TYPE_DYN_RECORD))
}
fn parse_tuple_type(&mut self) -> Option<CompletedMarker> {
if !self.at(L_PAREN) {
return None;
}
let m = self.start();
self.bump_any();
if self.eat(R_PAREN) {
return Some(m.complete(self, TYPE_TUPLE));
}
self.parse_type_with_opts(TypeOpts::default().allow_optional());
while self.eat(COMMA) {
if self.at(R_PAREN) {
break;
}
self.parse_type_with_opts(TypeOpts::default().allow_optional());
}
self.expect(R_PAREN);
Some(m.complete(self, TYPE_TUPLE))
}
fn parse_array_or_vector_type(&mut self) -> Option<CompletedMarker> {
if !self.at(L_BRACKET) {
return None;
}
let m = self.start();
self.bump_any();
self.parse_type_with_opts(TypeOpts::default().allow_optional());
if self.eat(SEMICOLON) {
let len = self.start();
self.parse_array_length();
len.complete(self, ARRAY_LENGTH);
self.expect(R_BRACKET);
return Some(m.complete(self, TYPE_ARRAY));
}
if self.at(COMMA) {
self.error("expected ';' for array type, found ','");
self.recover(&[R_BRACKET]);
self.eat(R_BRACKET);
return Some(m.complete(self, TYPE_ARRAY));
}
self.expect(R_BRACKET);
Some(m.complete(self, TYPE_VECTOR))
}
fn parse_array_length(&mut self) {
self.parse_expr();
}
fn parse_future_type(&mut self) -> Option<CompletedMarker> {
if !self.at(KW_FINAL_UPPER) {
return None;
}
let m = self.start();
self.bump_any();
if self.eat(LT) {
self.expect(KW_FN_UPPER);
self.expect(L_PAREN);
if !self.at(R_PAREN) {
self.parse_type_with_opts(TypeOpts::default().allow_optional());
while self.eat(COMMA) {
if self.at(R_PAREN) {
break;
}
self.parse_type_with_opts(TypeOpts::default().allow_optional());
}
}
self.expect(R_PAREN);
if self.eat(ARROW) {
self.parse_type_with_opts(TypeOpts::default().allow_optional());
}
self.expect(GT);
}
Some(m.complete(self, TYPE_FINAL))
}
fn parse_mapping_type(&mut self) -> Option<CompletedMarker> {
if !self.at(KW_MAPPING) {
return None;
}
let m = self.start();
self.bump_any();
self.parse_type();
self.expect(FAT_ARROW);
self.parse_type();
Some(m.complete(self, TYPE_MAPPING))
}
fn parse_named_type(&mut self) -> Option<CompletedMarker> {
if !self.at(IDENT) {
return None;
}
let m = self.start();
self.bump_any();
if self.at(DOT) && self.nth(1) == KW_ALEO {
self.bump_any(); self.bump_any();
if self.eat(COLON_COLON) {
if self.at(IDENT) {
self.bump_any();
while self.at(COLON_COLON) && self.nth(1) == IDENT {
self.bump_any(); self.bump_any(); }
} else {
self.error("expected type name after ::");
}
}
if self.at(COLON_COLON) && self.nth(1) == L_BRACKET {
self.bump_any(); self.parse_const_generic_args_bracket();
}
return Some(m.complete(self, TYPE_LOCATOR));
}
while self.eat(COLON_COLON) {
if self.at(L_BRACKET) {
self.parse_const_generic_args_bracket();
break;
} else if self.at(LT) {
self.parse_const_generic_args_angle();
break;
} else if self.at(IDENT) {
self.bump_any();
} else {
self.error("expected identifier, [, or < after ::");
break;
}
}
Some(m.complete(self, TYPE_PATH))
}
pub fn parse_const_param_list(&mut self) {
let m = self.start();
if !self.eat(L_BRACKET) {
m.abandon(self);
return;
}
if !self.at(R_BRACKET) {
self.parse_const_param();
while self.eat(COMMA) {
if self.at(R_BRACKET) {
break;
}
self.parse_const_param();
}
}
self.expect(R_BRACKET);
m.complete(self, CONST_PARAM_LIST);
}
fn parse_const_param(&mut self) {
let m = self.start();
self.skip_trivia();
if self.at(IDENT) {
self.bump_any(); } else {
self.error("expected const parameter name");
}
self.expect(COLON);
self.parse_type();
m.complete(self, CONST_PARAM);
}
pub fn parse_const_generic_args_bracket(&mut self) {
let m = self.start();
if !self.eat(L_BRACKET) {
m.abandon(self);
return;
}
if !self.at(R_BRACKET) {
self.parse_const_generic_arg();
while self.eat(COMMA) {
if self.at(R_BRACKET) {
break;
}
self.erroring = false;
self.parse_const_generic_arg();
}
}
self.expect(R_BRACKET);
m.complete(self, CONST_ARG_LIST);
}
pub fn parse_const_generic_args_angle(&mut self) {
let m = self.start();
if !self.eat(LT) {
m.abandon(self);
return;
}
if self.at(IDENT) || self.at(INTEGER) {
self.bump_any();
}
while self.eat(COMMA) {
if self.at(GT) {
break;
}
if self.at(IDENT) || self.at(INTEGER) {
self.bump_any();
} else {
self.error("expected const generic argument");
break;
}
}
self.expect(GT);
m.complete(self, CONST_ARG_LIST);
}
fn parse_const_generic_arg(&mut self) {
self.skip_trivia();
if matches!(self.current(), KW_PUBLIC | KW_PRIVATE | KW_CONSTANT) {
let m = self.start();
self.bump_any(); self.parse_type();
m.complete(self, DYNAMIC_CALL_RETURN_TYPE);
} else if self.at_primitive_type() {
self.parse_type();
} else if self.at(L_BRACKET) && self.nth(1).is_type_keyword() {
self.parse_type();
} else if self.at(KW_FINAL_UPPER) {
self.parse_type();
} else if self.at(KW_DYN) {
self.parse_type();
} else if self.at(L_PAREN) && (self.nth(1) == R_PAREN || Self::PRIMITIVE_TYPE_KINDS.contains(&self.nth(1))) {
self.parse_type();
} else {
self.parse_expr();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{lexer::lex, parser::Parse};
use expect_test::{Expect, expect};
fn check_type(input: &str, expect: Expect) {
let (tokens, _) = lex(input);
let mut parser = Parser::new(input, &tokens);
let root = parser.start();
parser.parse_type();
parser.skip_trivia();
root.complete(&mut parser, ROOT);
let parse: Parse = parser.finish(vec![]);
let output = format!("{:#?}", parse.syntax());
expect.assert_eq(&output);
}
fn check_type_optional(input: &str, expect: Expect) {
let (tokens, _) = lex(input);
let mut parser = Parser::new(input, &tokens);
let root = parser.start();
parser.parse_type_with_opts(TypeOpts::default().allow_optional());
parser.skip_trivia();
root.complete(&mut parser, ROOT);
let parse: Parse = parser.finish(vec![]);
let output = format!("{:#?}", parse.syntax());
expect.assert_eq(&output);
}
#[test]
fn parse_type_bool() {
check_type("bool", expect![[r#"
ROOT@0..4
TYPE_PRIMITIVE@0..4
KW_BOOL@0..4 "bool"
"#]]);
}
#[test]
fn parse_type_field() {
check_type("field", expect![[r#"
ROOT@0..5
TYPE_PRIMITIVE@0..5
KW_FIELD@0..5 "field"
"#]]);
}
#[test]
fn parse_type_group() {
check_type("group", expect![[r#"
ROOT@0..5
TYPE_PRIMITIVE@0..5
KW_GROUP@0..5 "group"
"#]]);
}
#[test]
fn parse_type_address() {
check_type("address", expect![[r#"
ROOT@0..7
TYPE_PRIMITIVE@0..7
KW_ADDRESS@0..7 "address"
"#]]);
}
#[test]
fn parse_type_scalar() {
check_type("scalar", expect![[r#"
ROOT@0..6
TYPE_PRIMITIVE@0..6
KW_SCALAR@0..6 "scalar"
"#]]);
}
#[test]
fn parse_type_signature() {
check_type("signature", expect![[r#"
ROOT@0..9
TYPE_PRIMITIVE@0..9
KW_SIGNATURE@0..9 "signature"
"#]]);
}
#[test]
fn parse_type_string() {
check_type("string", expect![[r#"
ROOT@0..6
TYPE_PRIMITIVE@0..6
KW_STRING@0..6 "string"
"#]]);
}
#[test]
fn parse_type_identifier() {
check_type("identifier", expect![[r#"
ROOT@0..10
TYPE_PRIMITIVE@0..10
KW_IDENTIFIER@0..10 "identifier"
"#]]);
}
#[test]
fn parse_type_u32() {
check_type("u32", expect![[r#"
ROOT@0..3
TYPE_PRIMITIVE@0..3
KW_U32@0..3 "u32"
"#]]);
}
#[test]
fn parse_type_i128() {
check_type("i128", expect![[r#"
ROOT@0..4
TYPE_PRIMITIVE@0..4
KW_I128@0..4 "i128"
"#]]);
}
#[test]
fn parse_type_unit() {
check_type("()", expect![[r#"
ROOT@0..2
TYPE_TUPLE@0..2
L_PAREN@0..1 "("
R_PAREN@1..2 ")"
"#]]);
}
#[test]
fn parse_type_tuple_single() {
check_type("(u32)", expect![[r#"
ROOT@0..5
TYPE_TUPLE@0..5
L_PAREN@0..1 "("
TYPE_PRIMITIVE@1..4
KW_U32@1..4 "u32"
R_PAREN@4..5 ")"
"#]]);
}
#[test]
fn parse_type_tuple_pair() {
check_type("(u32, field)", expect![[r#"
ROOT@0..12
TYPE_TUPLE@0..12
L_PAREN@0..1 "("
TYPE_PRIMITIVE@1..4
KW_U32@1..4 "u32"
COMMA@4..5 ","
WHITESPACE@5..6 " "
TYPE_PRIMITIVE@6..11
KW_FIELD@6..11 "field"
R_PAREN@11..12 ")"
"#]]);
}
#[test]
fn parse_type_tuple_trailing_comma() {
check_type("(u32, field,)", expect![[r#"
ROOT@0..13
TYPE_TUPLE@0..13
L_PAREN@0..1 "("
TYPE_PRIMITIVE@1..4
KW_U32@1..4 "u32"
COMMA@4..5 ","
WHITESPACE@5..6 " "
TYPE_PRIMITIVE@6..11
KW_FIELD@6..11 "field"
COMMA@11..12 ","
R_PAREN@12..13 ")"
"#]]);
}
#[test]
fn parse_type_array_fixed() {
check_type("[u32; 10]", expect![[r#"
ROOT@0..9
TYPE_ARRAY@0..9
L_BRACKET@0..1 "["
TYPE_PRIMITIVE@1..4
KW_U32@1..4 "u32"
SEMICOLON@4..5 ";"
ARRAY_LENGTH@5..8
WHITESPACE@5..6 " "
LITERAL_INT@6..8
INTEGER@6..8 "10"
R_BRACKET@8..9 "]"
"#]]);
}
#[test]
fn parse_type_array_const_generic() {
check_type("[field; N]", expect![[r#"
ROOT@0..10
TYPE_ARRAY@0..10
L_BRACKET@0..1 "["
TYPE_PRIMITIVE@1..6
KW_FIELD@1..6 "field"
SEMICOLON@6..7 ";"
ARRAY_LENGTH@7..9
WHITESPACE@7..8 " "
PATH_EXPR@8..9
IDENT@8..9 "N"
R_BRACKET@9..10 "]"
"#]]);
}
#[test]
fn parse_type_vector() {
check_type("[u8]", expect![[r#"
ROOT@0..4
TYPE_VECTOR@0..4
L_BRACKET@0..1 "["
TYPE_PRIMITIVE@1..3
KW_U8@1..3 "u8"
R_BRACKET@3..4 "]"
"#]]);
}
#[test]
fn parse_type_nested_array() {
check_type("[[u32; 3]; 2]", expect![[r#"
ROOT@0..13
TYPE_ARRAY@0..13
L_BRACKET@0..1 "["
TYPE_ARRAY@1..9
L_BRACKET@1..2 "["
TYPE_PRIMITIVE@2..5
KW_U32@2..5 "u32"
SEMICOLON@5..6 ";"
ARRAY_LENGTH@6..8
WHITESPACE@6..7 " "
LITERAL_INT@7..8
INTEGER@7..8 "3"
R_BRACKET@8..9 "]"
SEMICOLON@9..10 ";"
ARRAY_LENGTH@10..12
WHITESPACE@10..11 " "
LITERAL_INT@11..12
INTEGER@11..12 "2"
R_BRACKET@12..13 "]"
"#]]);
}
#[test]
fn parse_type_optional() {
check_type_optional("u32?", expect![[r#"
ROOT@0..4
TYPE_OPTIONAL@0..4
TYPE_PRIMITIVE@0..3
KW_U32@0..3 "u32"
QUESTION@3..4 "?"
"#]]);
}
#[test]
fn parse_type_optional_named() {
check_type_optional("Token?", expect![[r#"
ROOT@0..6
TYPE_OPTIONAL@0..6
TYPE_PATH@0..5
IDENT@0..5 "Token"
QUESTION@5..6 "?"
"#]]);
}
#[test]
fn parse_type_named_simple() {
check_type("Token", expect![[r#"
ROOT@0..5
TYPE_PATH@0..5
IDENT@0..5 "Token"
"#]]);
}
#[test]
fn parse_type_named_path() {
check_type("Foo::Bar", expect![[r#"
ROOT@0..8
TYPE_PATH@0..8
IDENT@0..3 "Foo"
COLON_COLON@3..5 "::"
IDENT@5..8 "Bar"
"#]]);
}
#[test]
fn parse_type_named_const_generic_bracket() {
check_type("Poseidon::[N]", expect![[r#"
ROOT@0..13
TYPE_PATH@0..13
IDENT@0..8 "Poseidon"
COLON_COLON@8..10 "::"
CONST_ARG_LIST@10..13
L_BRACKET@10..11 "["
PATH_EXPR@11..12
IDENT@11..12 "N"
R_BRACKET@12..13 "]"
"#]]);
}
#[test]
fn parse_type_named_const_generic_angle() {
check_type("Poseidon::<4>", expect![[r#"
ROOT@0..13
TYPE_PATH@0..13
IDENT@0..8 "Poseidon"
COLON_COLON@8..10 "::"
CONST_ARG_LIST@10..13
LT@10..11 "<"
INTEGER@11..12 "4"
GT@12..13 ">"
"#]]);
}
#[test]
fn parse_type_locator() {
check_type("credits.aleo::Token", expect![[r#"
ROOT@0..19
TYPE_LOCATOR@0..19
IDENT@0..7 "credits"
DOT@7..8 "."
KW_ALEO@8..12 "aleo"
COLON_COLON@12..14 "::"
IDENT@14..19 "Token"
"#]]);
}
#[test]
fn parse_type_program_id_without_type() {
check_type("credits.aleo", expect![[r#"
ROOT@0..12
TYPE_LOCATOR@0..12
IDENT@0..7 "credits"
DOT@7..8 "."
KW_ALEO@8..12 "aleo"
"#]]);
}
#[test]
fn parse_type_final_simple() {
check_type("Final", expect![[r#"
ROOT@0..5
TYPE_FINAL@0..5
KW_FINAL_UPPER@0..5 "Final"
"#]]);
}
#[test]
fn parse_type_final_explicit() {
check_type("Final<Fn(u32) -> field>", expect![[r#"
ROOT@0..23
TYPE_FINAL@0..23
KW_FINAL_UPPER@0..5 "Final"
LT@5..6 "<"
KW_FN_UPPER@6..8 "Fn"
L_PAREN@8..9 "("
TYPE_PRIMITIVE@9..12
KW_U32@9..12 "u32"
R_PAREN@12..13 ")"
WHITESPACE@13..14 " "
ARROW@14..16 "->"
WHITESPACE@16..17 " "
TYPE_PRIMITIVE@17..22
KW_FIELD@17..22 "field"
GT@22..23 ">"
"#]]);
}
#[test]
fn parse_type_final_no_params() {
check_type("Final<Fn() -> ()>", expect![[r#"
ROOT@0..17
TYPE_FINAL@0..17
KW_FINAL_UPPER@0..5 "Final"
LT@5..6 "<"
KW_FN_UPPER@6..8 "Fn"
L_PAREN@8..9 "("
R_PAREN@9..10 ")"
WHITESPACE@10..11 " "
ARROW@11..13 "->"
WHITESPACE@13..14 " "
TYPE_TUPLE@14..16
L_PAREN@14..15 "("
R_PAREN@15..16 ")"
GT@16..17 ">"
"#]]);
}
#[test]
fn parse_type_mapping() {
check_type("mapping address => u64", expect![[r#"
ROOT@0..22
TYPE_MAPPING@0..22
KW_MAPPING@0..7 "mapping"
WHITESPACE@7..8 " "
TYPE_PRIMITIVE@8..15
KW_ADDRESS@8..15 "address"
WHITESPACE@15..16 " "
FAT_ARROW@16..18 "=>"
WHITESPACE@18..19 " "
TYPE_PRIMITIVE@19..22
KW_U64@19..22 "u64"
"#]]);
}
fn check_type_no_errors(input: &str) {
let (tokens, _) = lex(input);
let mut parser = Parser::new(input, &tokens);
let root = parser.start();
parser.parse_type();
parser.skip_trivia();
root.complete(&mut parser, ROOT);
let parse: Parse = parser.finish(vec![]);
if !parse.errors().is_empty() {
for err in parse.errors() {
eprintln!("error at {:?}: {}", err.range, err.message);
}
eprintln!("tree:\n{:#?}", parse.syntax());
panic!("type parse had {} error(s)", parse.errors().len());
}
}
#[test]
fn parse_type_const_generic_expr_simple() {
check_type_no_errors("Foo::[3]");
}
#[test]
fn parse_type_const_generic_expr_add() {
check_type_no_errors("Foo::[N + 1]");
}
#[test]
fn parse_type_const_generic_expr_mul() {
check_type_no_errors("Foo::[2 * N]");
}
#[test]
fn parse_type_const_generic_multi_args() {
check_type_no_errors("Matrix::[M, K, N]");
}
#[test]
fn parse_type_const_generic_type_arg() {
check_type_no_errors("Deserialize::[u32]");
}
#[test]
fn parse_type_const_generic_array_type_arg() {
check_type_no_errors("Deserialize::[[u8; 4]]");
}
#[test]
fn parse_type_locator_const_generic() {
check_type_no_errors("child.aleo::Bar::[4]");
}
#[test]
fn parse_type_tuple_nested() {
check_type("((u32, u64), bool)", expect![[r#"
ROOT@0..18
TYPE_TUPLE@0..18
L_PAREN@0..1 "("
TYPE_TUPLE@1..11
L_PAREN@1..2 "("
TYPE_PRIMITIVE@2..5
KW_U32@2..5 "u32"
COMMA@5..6 ","
WHITESPACE@6..7 " "
TYPE_PRIMITIVE@7..10
KW_U64@7..10 "u64"
R_PAREN@10..11 ")"
COMMA@11..12 ","
WHITESPACE@12..13 " "
TYPE_PRIMITIVE@13..17
KW_BOOL@13..17 "bool"
R_PAREN@17..18 ")"
"#]]);
}
#[test]
fn parse_type_optional_array() {
check_type_optional("[u32; 3]?", expect![[r#"
ROOT@0..9
TYPE_OPTIONAL@0..9
TYPE_ARRAY@0..8
L_BRACKET@0..1 "["
TYPE_PRIMITIVE@1..4
KW_U32@1..4 "u32"
SEMICOLON@4..5 ";"
ARRAY_LENGTH@5..7
WHITESPACE@5..6 " "
LITERAL_INT@6..7
INTEGER@6..7 "3"
R_BRACKET@7..8 "]"
QUESTION@8..9 "?"
"#]]);
}
#[test]
fn parse_type_optional_tuple() {
check_type_optional("(u32, u64)?", expect![[r#"
ROOT@0..11
TYPE_OPTIONAL@0..11
TYPE_TUPLE@0..10
L_PAREN@0..1 "("
TYPE_PRIMITIVE@1..4
KW_U32@1..4 "u32"
COMMA@4..5 ","
WHITESPACE@5..6 " "
TYPE_PRIMITIVE@6..9
KW_U64@6..9 "u64"
R_PAREN@9..10 ")"
QUESTION@10..11 "?"
"#]]);
}
#[test]
fn parse_type_final_multi_param() {
check_type("Final<Fn(u32, field, group) -> (u32, u64)>", expect![[r#"
ROOT@0..42
TYPE_FINAL@0..42
KW_FINAL_UPPER@0..5 "Final"
LT@5..6 "<"
KW_FN_UPPER@6..8 "Fn"
L_PAREN@8..9 "("
TYPE_PRIMITIVE@9..12
KW_U32@9..12 "u32"
COMMA@12..13 ","
WHITESPACE@13..14 " "
TYPE_PRIMITIVE@14..19
KW_FIELD@14..19 "field"
COMMA@19..20 ","
WHITESPACE@20..21 " "
TYPE_PRIMITIVE@21..26
KW_GROUP@21..26 "group"
R_PAREN@26..27 ")"
WHITESPACE@27..28 " "
ARROW@28..30 "->"
WHITESPACE@30..31 " "
TYPE_TUPLE@31..41
L_PAREN@31..32 "("
TYPE_PRIMITIVE@32..35
KW_U32@32..35 "u32"
COMMA@35..36 ","
WHITESPACE@36..37 " "
TYPE_PRIMITIVE@37..40
KW_U64@37..40 "u64"
R_PAREN@40..41 ")"
GT@41..42 ">"
"#]]);
}
#[test]
fn parse_type_const_generic_expr_sub() {
check_type("Foo::[N - 1]", expect![[r#"
ROOT@0..12
TYPE_PATH@0..12
IDENT@0..3 "Foo"
COLON_COLON@3..5 "::"
CONST_ARG_LIST@5..12
L_BRACKET@5..6 "["
BINARY_EXPR@6..11
PATH_EXPR@6..8
IDENT@6..7 "N"
WHITESPACE@7..8 " "
MINUS@8..9 "-"
WHITESPACE@9..10 " "
LITERAL_INT@10..11
INTEGER@10..11 "1"
R_BRACKET@11..12 "]"
"#]]);
}
}