///|
/// Class and function definition parsing for line blocks.
///|
fn parse_class_stmt(
parser : LineParser,
stmts : Array[Stmt],
pending_decorators : Array[Expr],
pending_decorator_token : Token?,
line_text : String,
line_index : Int32,
current_indent : Int32,
next_index : Int32,
) -> Result[(Bool, Array[Expr], Token?), ParseError] {
if !line_text.has_prefix("class ") {
return Ok((false, pending_decorators, pending_decorator_token))
}
let decorators = pending_decorators
let next_pending : Array[Expr] = []
let next_token : Token? = None
let colon_pos = find_top_level_colon(line_text)
if colon_pos is None {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent,
end: parser.offsets[parser.index] + current_indent + 1,
line: parser.index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let header = substring(line_text, 6, colon_pos.unwrap())
.trim(chars=" \t\n\r")
.to_owned()
let paren_span = find_top_level_paren_span(header)
let name_part = match paren_span {
Some(span) => substring(header, 0, span.0).trim(chars=" \t\n\r").to_owned()
None => header
}
let name_token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent + 6,
end: parser.offsets[parser.index] + current_indent + 7,
line: parser.index + 1,
column: current_indent + 7,
}
let (name, type_params) = match
parse_type_param_header(name_part, name_token) {
Ok(value) => value
Err(err) => return Err(err)
}
let bases : Array[Expr] = []
let keywords : Array[(String, Expr)] = []
if paren_span is Some(span) {
let open_pos = span.0
let close_pos = span.1
let tail = substring(header, close_pos + 1, header.length())
.trim(chars=" \t\n\r")
.to_owned()
if tail.length() > 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
name_token,
),
)
}
let bases_text = substring(header, open_pos + 1, close_pos)
.trim(chars=" \t\n\r")
.to_owned()
if bases_text.length() > 0 {
let base_parts = split_top_level_commas(bases_text)
for part in base_parts {
let expr_text = part.trim(chars=" \t\n\r").to_owned()
if expr_text.length() == 0 {
continue
}
if expr_text.has_prefix("**") {
let inner = substring(expr_text, 2, expr_text.length())
.trim(chars=" \t\n\r")
.to_owned()
if inner.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
name_token,
),
)
}
match parse_expr(inner) {
Ok(expr) => keywords.push(("**", expr))
Err(_) =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
name_token,
),
)
}
continue
}
if expr_text.has_prefix("*") {
let inner = substring(expr_text, 1, expr_text.length())
.trim(chars=" \t\n\r")
.to_owned()
if inner.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
name_token,
),
)
}
match parse_expr(inner) {
Ok(expr) => bases.push(Expr::Starred(value=expr))
Err(_) =>
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
name_token,
),
)
}
continue
}
if has_bare_assign(expr_text) {
let eq_pos = find_char(expr_text, '=')
if eq_pos is Some(eq_idx) {
let key = substring(expr_text, 0, eq_idx)
.trim(chars=" \t\n\r")
.to_owned()
let value_text = substring(
expr_text,
eq_idx + 1,
expr_text.length(),
)
.trim(chars=" \t\n\r")
.to_owned()
if key.length() == 0 || value_text.length() == 0 {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent + 6,
end: parser.offsets[parser.index] + current_indent + 7,
line: parser.index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
match parse_expr(value_text) {
Ok(expr) => keywords.push((key, expr))
Err(_) => {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent + 6,
end: parser.offsets[parser.index] + current_indent + 7,
line: parser.index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
continue
} else {
continue
}
}
match parse_expr(expr_text) {
Ok(expr) => bases.push(expr)
Err(_) => {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent + 6,
end: parser.offsets[parser.index] + current_indent + 7,
line: parser.index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
}
}
}
if name.length() == 0 {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent,
end: parser.offsets[parser.index] + current_indent + 1,
line: parser.index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
if is_async_await_reserved_name(name) {
return Err(parser.invalid_syntax_at_line(parser.index + 1))
}
let body_text = strip_inline_comment(
substring(line_text, colon_pos.unwrap() + 1, line_text.length()).to_string(),
)
.trim(chars=" \t")
.to_owned()
if body_text.length() > 0 {
let body : Array[Stmt] = []
let segments = split_statements(body_text)
for segment_pair in segments {
let segment = segment_pair.0
let offset = segment_pair.1
if segment.length() == 0 {
continue
}
let base_offset = parser.offsets[parser.index] +
current_indent +
colon_pos.unwrap() +
1 +
offset
let parsed = match
parse_simple_statement(
segment,
parser.index,
parser.offsets[parser.index],
base_offset,
parser.async_depth > 0,
) {
Ok(value) => value
Err(err) => return Err(err)
}
for stmt in parsed {
body.push(stmt)
}
}
if class_scope_error_line(body, parser.func_depth > 0) is Some(line) {
return Err(parser.invalid_syntax_at_line(line))
}
match stmts_first_illegal_await_line(body) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
stmts.push(
wrap_stmt_for_line(
parser.offsets,
line_index,
current_indent,
Stmt::Class(name~, decorators~, type_params~, bases~, keywords~, body~),
),
)
parser.index = next_index
return Ok((true, next_pending, next_token))
}
parser.index = next_index
let next_indent = parser.peek_next_indent()
if next_indent <= current_indent {
let missing_line = parser.clamp_line_index(
parser.peek_next_significant_index().unwrap_or(parser.index),
)
let token = {
kind: Eof,
start: parser.offsets[missing_line],
end: parser.offsets[missing_line] + 1,
line: missing_line + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Indentation,
"expected indented block".to_string(),
token,
),
)
}
let body = match parser.parse_block(next_indent) {
Ok(value) => value
Err(err) => return Err(err)
}
if class_scope_error_line(body, parser.func_depth > 0) is Some(line) {
return Err(parser.invalid_syntax_at_line(line))
}
match stmts_first_illegal_await_line(body) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
stmts.push(
wrap_stmt_for_line(
parser.offsets,
line_index,
current_indent,
Stmt::Class(name~, decorators~, type_params~, bases~, keywords~, body~),
),
)
Ok((true, next_pending, next_token))
}
///|
fn parse_def_stmt(
parser : LineParser,
stmts : Array[Stmt],
pending_decorators : Array[Expr],
pending_decorator_token : Token?,
line_text_in : String,
line : String,
line_index : Int32,
current_indent : Int32,
next_index : Int32,
) -> Result[(Bool, Array[Expr], Token?), ParseError] {
let mut line_text = line_text_in
let mut is_async_def = false
if line_text.has_prefix("async def ") {
is_async_def = true
line_text = "def " + substring(line_text, 10, line_text.length())
}
if !line_text.has_prefix("def ") {
return Ok((false, pending_decorators, pending_decorator_token))
}
let decorators = pending_decorators
let next_pending : Array[Expr] = []
let next_token : Token? = None
let header = substring(line_text, 4, line_text.length())
let paren_span = find_top_level_paren_span(header)
let close_pos = match paren_span {
Some(span) => Some(span.1)
None => None
}
let colon_pos = find_top_level_colon(header)
if paren_span is None || close_pos is None || colon_pos is None {
let token = {
kind: Eof,
start: parser.offsets[parser.index],
end: parser.offsets[parser.index] + line.length(),
line: parser.index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid function definition".to_string(),
token,
),
)
}
let name_part = match paren_span {
Some(span) => substring(header, 0, span.0).trim(chars=" \t").to_owned()
None => "".to_string()
}
let name_token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent + 4,
end: parser.offsets[parser.index] + current_indent + 5,
line: parser.index + 1,
column: current_indent + 5,
}
let (name, type_params) = match
parse_type_param_header(name_part, name_token) {
Ok(value) => value
Err(err) => return Err(err)
}
let name_chars = name.to_array()
for i = 0; i < name_chars.length(); i = i + 1 {
let c = name_chars[i]
if !(is_alnum(c) || c == '_') || (i == 0 && is_digit(c)) {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent + 4 + i,
end: parser.offsets[parser.index] + current_indent + 4 + i + 1,
line: parser.index + 1,
column: current_indent + 4 + i + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
}
if is_async_await_reserved_name(name) {
return Err(parser.invalid_syntax_at_line(parser.index + 1))
}
let name_end = match paren_span {
Some(span) => span.0
None => 0
}
let params_text = substring(header, name_end + 1, close_pos.unwrap())
let params : Array[String] = []
let defaults : Array[Expr] = []
let annotations : Array[(String, Expr)] = []
let mut return_annotation : Expr? = None
let seen : Array[String] = []
let mut default_seen = false
let mut kw_only = false
let mut seen_posonly_marker = false
let mut seen_bare_star = false
let mut bare_star_has_kwonly_name = false
let params_chars = params_text.to_array()
let mut param_start = 0
let mut idx = 0
let mut quote : Char? = None
let mut escaped = false
let mut depth = 0
let param_base = current_indent + 4 + name_end + 1
fn is_valid_param_name(text : String) -> Bool {
if text.length() == 0 {
return false
}
if text == "None" || text == "True" || text == "False" {
return false
}
let chars = text.to_array()
for i = 0; i < chars.length(); i = i + 1 {
let c = chars[i]
if !(is_alnum(c) || c == '_') || (i == 0 && is_digit(c)) {
return false
}
}
true
}
fn consume_param_segment(end_idx : Int32) -> Result[Unit, ParseError] {
let raw_param = substring(params_text, param_start, end_idx)
let mut leading = 0
let raw_chars = raw_param.to_array()
while leading < raw_chars.length() && raw_chars[leading] == ' ' {
leading += 1
}
let trimmed_param = raw_param.trim(chars=" \t\n\r").to_owned()
if trimmed_param.length() == 0 {
return Ok(())
}
let mut colon_idx_opt : Int32? = None
let mut eq_idx_opt : Int32? = None
let scan_chars = raw_param.to_array()
let mut scan_depth = 0
let mut scan_quote : Char? = None
let mut scan_escape = false
for j = 0; j < scan_chars.length(); j = j + 1 {
let c = scan_chars[j]
match scan_quote {
Some(q) =>
if scan_escape {
scan_escape = false
} else if c == '\\' {
scan_escape = true
} else if c == q {
scan_quote = None
}
None =>
if c == '\'' || c == '"' {
scan_quote = Some(c)
scan_escape = false
} else if c == '(' || c == '[' || c == '{' {
scan_depth += 1
} else if c == ')' || c == ']' || c == '}' {
if scan_depth > 0 {
scan_depth -= 1
}
} else if c == ':' &&
scan_depth == 0 &&
colon_idx_opt is None &&
eq_idx_opt is None {
colon_idx_opt = Some(j)
} else if c == '=' && scan_depth == 0 && eq_idx_opt is None {
eq_idx_opt = Some(j)
}
}
}
let name_end_idx = match (colon_idx_opt, eq_idx_opt) {
(Some(cidx), Some(eidx)) => if cidx < eidx { cidx } else { eidx }
(Some(cidx), None) => cidx
(None, Some(eidx)) => eidx
(None, None) => raw_param.length()
}
let mut name = substring(raw_param, 0, name_end_idx)
.trim(chars=" \t\n\r")
.to_owned()
let has_default = eq_idx_opt is Some(_)
if name == "/" {
if has_default || kw_only || seen_posonly_marker {
let token = {
kind: Eof,
start: parser.offsets[parser.index] +
param_base +
param_start +
leading,
end: parser.offsets[parser.index] +
param_base +
param_start +
leading +
1,
line: parser.index + 1,
column: param_base + param_start + leading + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
seen_posonly_marker = true
params.push("/")
return Ok(())
}
if name == "*" {
if seen_bare_star || kw_only {
let token = {
kind: Eof,
start: parser.offsets[parser.index] +
param_base +
param_start +
leading,
end: parser.offsets[parser.index] +
param_base +
param_start +
leading +
1,
line: parser.index + 1,
column: param_base + param_start + leading + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
seen_bare_star = true
kw_only = true
params.push("*")
name = ""
}
let mut star_count = 0
while name.has_prefix("*") {
star_count += 1
name = substring(name, 1, name.length()).to_string()
name = name.trim(chars=" \t\n\r").to_owned()
}
if star_count > 0 {
kw_only = true
}
if name.length() == 0 {
return Ok(())
}
let param_offset = param_start + leading
if seen_bare_star && star_count == 2 && !bare_star_has_kwonly_name {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + param_base + param_offset,
end: parser.offsets[parser.index] + param_base + param_offset + 1,
line: parser.index + 1,
column: param_base + param_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
if is_async_await_reserved_name(name) {
return Err(parser.invalid_syntax_at_line(parser.index + 1))
}
if !is_valid_param_name(name) {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + param_base + param_offset,
end: parser.offsets[parser.index] + param_base + param_offset + 1,
line: parser.index + 1,
column: param_base + param_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
for existing in seen {
if existing == name {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + param_base + param_offset,
end: parser.offsets[parser.index] + param_base + param_offset + 1,
line: parser.index + 1,
column: param_base + param_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"duplicate argument '" + name + "' in function definition",
token,
),
)
}
}
if star_count > 2 {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + param_base + param_offset,
end: parser.offsets[parser.index] + param_base + param_offset + 1,
line: parser.index + 1,
column: param_base + param_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
if star_count > 0 && has_default {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + param_base + param_offset,
end: parser.offsets[parser.index] + param_base + param_offset + 1,
line: parser.index + 1,
column: param_base + param_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
if !kw_only && default_seen && !has_default {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + param_base + param_offset,
end: parser.offsets[parser.index] + param_base + param_offset + 1,
line: parser.index + 1,
column: param_base + param_offset + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"non-default argument follows default argument".to_string(),
token,
),
)
}
if has_default {
if !kw_only {
default_seen = true
}
match eq_idx_opt {
Some(eq_idx) => {
let default_text = substring(
raw_param,
eq_idx + 1,
raw_param.length(),
)
.trim(chars=" \t\n\r")
.to_owned()
let tokens = match
tokenize_expr(
default_text,
parser.index + 1,
parser.offsets[parser.index] +
param_base +
param_start +
eq_idx +
1,
param_base + param_start + eq_idx + 2,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
if expr_has_illegal_await(expr, false) {
return Err(parser.invalid_syntax_at_line(parser.index + 1))
}
defaults.push(expr)
}
None => ()
}
}
match colon_idx_opt {
Some(colon_idx) => {
let ann_end = match eq_idx_opt {
Some(eq_idx) =>
if eq_idx > colon_idx {
eq_idx
} else {
raw_param.length()
}
None => raw_param.length()
}
let ann_raw = substring(raw_param, colon_idx + 1, ann_end).to_string()
let ann_text = ann_raw.trim(chars=" \t\n\r").to_owned()
if ann_text.length() == 0 {
let token = {
kind: Eof,
start: parser.offsets[parser.index] +
param_base +
param_start +
colon_idx,
end: parser.offsets[parser.index] +
param_base +
param_start +
colon_idx +
1,
line: parser.index + 1,
column: param_base + param_start + colon_idx + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
let ann_leading = count_indent(ann_raw)
let ann_offset = parser.offsets[parser.index] +
param_base +
param_start +
colon_idx +
1 +
ann_leading
let ann_column = param_base +
param_start +
colon_idx +
1 +
ann_leading +
1
let tokens = match
tokenize_expr(ann_text, parser.index + 1, ann_offset, ann_column) {
Ok(value) => value
Err(err) => return Err(err)
}
// PEP 646 permits an unpacked annotation on a variadic positional
// parameter: def f(*args: *Ts). It is not a general expression form.
let expr = if star_count == 1 &&
tokens.length() > 0 &&
tokens[0].kind == Star {
match parse_tokens_expr(tokens[1:].to_owned()) {
Ok(value) => Expr::Starred(value~)
Err(err) => return Err(err)
}
} else {
match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
}
if expr_has_illegal_await(expr, false) {
return Err(parser.invalid_syntax_at_line(parser.index + 1))
}
annotations.push((name, expr))
}
None => ()
}
seen.push(name)
let mut encoded = name
if star_count == 1 {
encoded = "*" + name
} else if star_count == 2 {
encoded = "**" + name
} else if has_default {
encoded = name + "="
}
if seen_bare_star && star_count == 0 {
bare_star_has_kwonly_name = true
}
params.push(encoded)
Ok(())
}
while idx < params_chars.length() {
let c = params_chars[idx]
match quote {
Some(q) =>
if escaped {
escaped = false
} else if c == '\\' {
escaped = true
} else if c == q {
quote = None
}
None =>
if c == '\'' || c == '"' {
quote = Some(c)
} else if c == '(' || c == '[' || c == '{' {
depth += 1
} else if c == ')' || c == ']' || c == '}' {
if depth > 0 {
depth -= 1
}
} else if c == ',' && depth == 0 {
let _ = match consume_param_segment(idx) {
Ok(_) => ()
Err(err) => return Err(err)
}
param_start = idx + 1
}
}
idx += 1
}
let _ = match consume_param_segment(params_chars.length()) {
Ok(_) => ()
Err(err) => return Err(err)
}
if seen_bare_star && !bare_star_has_kwonly_name {
return Err(parser.invalid_syntax_at_line(parser.index + 1))
}
let return_slice = substring(
header,
close_pos.unwrap() + 1,
colon_pos.unwrap(),
).to_string()
match return_slice.find("->") {
Some(pos) => {
let raw_ret = substring(return_slice, pos + 2, return_slice.length()).to_string()
let ret_text = raw_ret.trim(chars=" \t\n\r").to_owned()
if ret_text.length() == 0 {
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
name_token,
),
)
}
let ret_leading = count_indent(raw_ret)
let ret_offset = parser.offsets[parser.index] +
current_indent +
4 +
close_pos.unwrap() +
1 +
pos +
2 +
ret_leading
let ret_column = current_indent +
4 +
close_pos.unwrap() +
1 +
pos +
2 +
ret_leading +
1
let tokens = match
tokenize_expr(ret_text, parser.index + 1, ret_offset, ret_column) {
Ok(value) => value
Err(err) => return Err(err)
}
let expr = match parse_tokens_expr(tokens) {
Ok(value) => value
Err(err) => return Err(err)
}
return_annotation = Some(expr)
}
None => ()
}
let body_text = strip_inline_comment(
substring(header, colon_pos.unwrap() + 1, header.length()).to_string(),
)
.trim(chars=" \t")
.to_owned()
if !is_async_def && line_has_await_keyword(body_text) {
let token = {
kind: Eof,
start: parser.offsets[parser.index] + current_indent,
end: parser.offsets[parser.index] + current_indent + 1,
line: parser.index + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"invalid syntax".to_string(),
token,
),
)
}
if body_text.length() > 0 {
let body : Array[Stmt] = []
let segments = split_statements(body_text)
for segment_pair in segments {
let segment = segment_pair.0
let offset = segment_pair.1
if segment.length() == 0 {
continue
}
let base_offset = parser.offsets[parser.index] +
current_indent +
4 +
colon_pos.unwrap() +
1 +
offset
let parsed = match
parse_simple_statement(
segment,
parser.index,
parser.offsets[parser.index],
base_offset,
parser.async_depth > 0,
) {
Ok(value) => value
Err(err) => return Err(err)
}
for stmt in parsed {
body.push(stmt)
}
}
match scope_decl_after_use_line(body) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
match stmts_first_param_scope_decl_line(body, params) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
for stmt in body {
match stmt {
Stmt::WithSpan(span=_, stmt=inner) =>
match inner {
Stmt::Global(names) =>
for name in names {
for param in params {
match normalize_param_name_for_compare(param) {
Some(param_name) =>
if param_name == name {
let token = {
kind: Eof,
start: parser.offsets[parser.index] +
current_indent +
4 +
colon_pos.unwrap() +
2,
end: parser.offsets[parser.index] +
current_indent +
4 +
colon_pos.unwrap() +
3,
line: parser.index + 1,
column: current_indent + 4 + colon_pos.unwrap() + 3,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"name '" + name + "' is parameter and global",
token,
),
)
}
None => ()
}
}
}
_ => ()
}
Stmt::Global(names) =>
for name in names {
for param in params {
match normalize_param_name_for_compare(param) {
Some(param_name) =>
if param_name == name {
let token = {
kind: Eof,
start: parser.offsets[parser.index] +
current_indent +
4 +
colon_pos.unwrap() +
2,
end: parser.offsets[parser.index] +
current_indent +
4 +
colon_pos.unwrap() +
3,
line: parser.index + 1,
column: current_indent + 4 + colon_pos.unwrap() + 3,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"name '" + name + "' is parameter and global",
token,
),
)
}
None => ()
}
}
}
_ => ()
}
}
if !is_async_def {
match stmts_first_illegal_await_line(body) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
}
let mut is_generator = false
for stmt in body {
match stmt {
Stmt::WithSpan(span=_, stmt=inner) =>
match inner {
Stmt::Yield(_) | Stmt::YieldFrom(_) => is_generator = true
_ => ()
}
Stmt::Yield(_) | Stmt::YieldFrom(_) => is_generator = true
_ => ()
}
}
stmts.push(
wrap_stmt_for_line(
parser.offsets,
line_index,
current_indent,
Stmt::Function(
name~,
decorators~,
type_params~,
params~,
defaults~,
annotations~,
return_annotation~,
body~,
is_generator~,
is_async=is_async_def,
),
),
)
parser.index = next_index
return Ok((true, next_pending, next_token))
}
parser.index = next_index
let body_start_index = parser.index
let next_indent = parser.peek_next_indent()
if next_indent <= current_indent {
let missing_line = parser.clamp_line_index(
parser.peek_next_significant_index().unwrap_or(parser.index),
)
let token = {
kind: Eof,
start: parser.offsets[missing_line],
end: parser.offsets[missing_line] + 1,
line: missing_line + 1,
column: 1,
}
return Err(
make_parse_error(
ParseErrorKind::Indentation,
"expected indented block".to_string(),
token,
),
)
}
let (body, _) = if is_async_def {
let mut idx = parser.index
while idx < parser.lines.length() {
let body_line = parser.lines[idx]
let trimmed = body_line.trim(chars=" \t").to_owned()
if trimmed.length() == 0 || trimmed.has_prefix("#") {
idx += 1
continue
}
let body_indent = count_indent(body_line)
if body_indent < next_indent {
break
}
if trimmed.has_prefix("def ") ||
trimmed.has_prefix("async def ") ||
trimmed.has_prefix("class ") {
let def_indent = body_indent
idx += 1
while idx < parser.lines.length() {
let next_line = parser.lines[idx]
let next_trimmed = next_line.trim(chars=" \t").to_owned()
if next_trimmed.length() == 0 || next_trimmed.has_prefix("#") {
idx += 1
continue
}
if count_indent(next_line) <= def_indent {
break
}
idx += 1
}
continue
}
if trimmed.has_prefix("yield from") {
let token = {
kind: Eof,
start: parser.offsets[idx] + body_indent,
end: parser.offsets[idx] + body_indent + 1,
line: idx + 1,
column: body_indent + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"'yield from' inside async function".to_string(),
token,
),
)
}
idx += 1
}
let prev_async_depth = parser.async_depth
parser.async_depth = 1
parser.func_depth += 1
let parsed = match parser.parse_block(next_indent) {
Ok(value) => value
Err(err) => {
parser.func_depth -= 1
parser.async_depth = prev_async_depth
return Err(err)
}
}
parser.func_depth -= 1
parser.async_depth = prev_async_depth
(parsed, false)
} else {
let prev_async_depth = parser.async_depth
parser.async_depth = 0
parser.func_depth += 1
let parsed = match parser.parse_block(next_indent) {
Ok(value) => value
Err(err) => {
parser.func_depth -= 1
parser.async_depth = prev_async_depth
return Err(err)
}
}
parser.func_depth -= 1
parser.async_depth = prev_async_depth
(parsed, false)
}
let has_yield_ref : Ref[Bool] = { val: false, }
let has_yield_from_ref : Ref[Bool] = { val: false, }
let has_return_value_ref : Ref[Bool] = { val: false, }
if !is_async_def {
match stmts_first_illegal_await_line(body) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
}
fn scan_expr_for_yield(
expr : Expr,
has_yield_ref : Ref[Bool],
has_yield_from_ref : Ref[Bool],
) -> Unit {
match expr {
Expr::WithSpan(span=_, expr=inner) =>
scan_expr_for_yield(inner, has_yield_ref, has_yield_from_ref)
Expr::Yield(value) => {
has_yield_ref.val = true
match value {
Some(inner) =>
scan_expr_for_yield(inner, has_yield_ref, has_yield_from_ref)
None => ()
}
}
Expr::YieldFrom(value) => {
has_yield_ref.val = true
has_yield_from_ref.val = true
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
}
Expr::Tuple(items) | Expr::List(items) | Expr::Set(items) =>
for item in items {
scan_expr_for_yield(item, has_yield_ref, has_yield_from_ref)
}
Expr::Dict(pairs) =>
for pair in pairs {
scan_expr_for_yield(pair.0, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(pair.1, has_yield_ref, has_yield_from_ref)
}
Expr::ListComp(elt~, clauses~, filters~)
| Expr::GenExp(elt~, clauses~, filters~)
| Expr::SetComp(elt~, clauses~, filters~) => {
scan_expr_for_yield(elt, has_yield_ref, has_yield_from_ref)
for clause in clauses {
scan_expr_for_yield(clause.iter, has_yield_ref, has_yield_from_ref)
}
for filter_expr in filters {
scan_expr_for_yield(filter_expr, has_yield_ref, has_yield_from_ref)
}
}
Expr::DictComp(key~, value~, clauses~, filters~) => {
scan_expr_for_yield(key, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
for clause in clauses {
scan_expr_for_yield(clause.iter, has_yield_ref, has_yield_from_ref)
}
for filter_expr in filters {
scan_expr_for_yield(filter_expr, has_yield_ref, has_yield_from_ref)
}
}
Expr::Lambda(..) => ()
Expr::IfExpr(condition~, then_expr~, else_expr~) => {
scan_expr_for_yield(condition, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(then_expr, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(else_expr, has_yield_ref, has_yield_from_ref)
}
Expr::NamedExpr(name=_, value~) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
Expr::Await(value) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
Expr::Slice(start~, end~, step~) => {
match start {
Some(value) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
None => ()
}
match end {
Some(value) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
None => ()
}
match step {
Some(value) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
None => ()
}
}
Expr::Attribute(value~, attr=_) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
Expr::Subscript(value~, index~) => {
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(index, has_yield_ref, has_yield_from_ref)
}
Expr::Call(callee~, args~) => {
scan_expr_for_yield(callee, has_yield_ref, has_yield_from_ref)
for arg in args {
scan_expr_for_yield(arg, has_yield_ref, has_yield_from_ref)
}
}
Expr::Keyword(name=_, value~) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
Expr::Starred(value~) | Expr::DoubleStarred(value~) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
Expr::Unary(op=_, expr~) =>
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
Expr::Binary(op=_, left~, right~) => {
scan_expr_for_yield(left, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(right, has_yield_ref, has_yield_from_ref)
}
Expr::BoolOp(op=_, values~) =>
for value in values {
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
}
Expr::Compare(left~, ops=_, comparators~) => {
scan_expr_for_yield(left, has_yield_ref, has_yield_from_ref)
for value in comparators {
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
}
}
_ => ()
}
}
fn scan_target_for_yield(
target : Target,
has_yield_ref : Ref[Bool],
has_yield_from_ref : Ref[Bool],
) -> Unit {
match target {
Target::Attribute(value~, attr=_) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
Target::Subscript(value~, index~) => {
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(index, has_yield_ref, has_yield_from_ref)
}
Target::Tuple(items) | Target::List(items) =>
for item in items {
scan_target_for_yield(item, has_yield_ref, has_yield_from_ref)
}
Target::Starred(inner) =>
scan_target_for_yield(inner, has_yield_ref, has_yield_from_ref)
_ => ()
}
}
fn scan_stmt_for_yield(
stmt : Stmt,
has_yield_ref : Ref[Bool],
has_yield_from_ref : Ref[Bool],
has_return_value_ref : Ref[Bool],
) -> Unit {
match stmt {
Stmt::WithSpan(span=_, stmt=inner) =>
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
Stmt::ExprStmt(expr) =>
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
Stmt::Assign(target~, value~) => {
scan_target_for_yield(target, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
}
Stmt::AugAssign(target~, op=_, value~) => {
scan_target_for_yield(target, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
}
Stmt::TypeAlias(name=_, type_params=_, value~) =>
scan_expr_for_yield(value, has_yield_ref, has_yield_from_ref)
Stmt::Return(value) =>
match value {
Some(expr) => {
has_return_value_ref.val = true
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
}
None => ()
}
Stmt::Yield(value) => {
has_yield_ref.val = true
match value {
Some(expr) =>
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
None => ()
}
}
Stmt::YieldFrom(expr) => {
has_yield_ref.val = true
has_yield_from_ref.val = true
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
}
Stmt::If(condition~, body~, else_body~) => {
scan_expr_for_yield(condition, has_yield_ref, has_yield_from_ref)
for inner in body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
for inner in else_body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
}
Stmt::Match(subject~, cases~) => {
scan_expr_for_yield(subject, has_yield_ref, has_yield_from_ref)
for case_item in cases {
let case_guard = case_item.1
let case_body = case_item.2
match case_guard {
Some(expr) =>
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
None => ()
}
for inner in case_body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
}
}
Stmt::While(condition~, body~, else_body~) => {
scan_expr_for_yield(condition, has_yield_ref, has_yield_from_ref)
for inner in body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
for inner in else_body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
}
Stmt::For(target~, iter~, body~, else_body~)
| Stmt::AsyncFor(target~, iter~, body~, else_body~) => {
scan_target_for_yield(target, has_yield_ref, has_yield_from_ref)
scan_expr_for_yield(iter, has_yield_ref, has_yield_from_ref)
for inner in body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
for inner in else_body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
}
Stmt::Assert(condition~, message~) => {
scan_expr_for_yield(condition, has_yield_ref, has_yield_from_ref)
match message {
Some(expr) =>
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
None => ()
}
}
Stmt::Raise(exc~, cause~) => {
match exc {
Some(expr) =>
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
None => ()
}
match cause {
Some(expr) =>
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
None => ()
}
}
Stmt::Del(targets) =>
for target in targets {
scan_target_for_yield(target, has_yield_ref, has_yield_from_ref)
}
Stmt::With(context~, target=_, body~)
| Stmt::AsyncWith(context~, target=_, body~) => {
scan_expr_for_yield(context, has_yield_ref, has_yield_from_ref)
for inner in body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
}
Stmt::Try(body~, handlers~, else_body~, finally_body~) => {
for inner in body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
for handler in handlers {
match handler.exc {
Some(expr) =>
scan_expr_for_yield(expr, has_yield_ref, has_yield_from_ref)
None => ()
}
for inner in handler.body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
}
for inner in else_body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
for inner in finally_body {
scan_stmt_for_yield(
inner, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
}
Stmt::Function(..) | Stmt::Class(..) => ()
_ => ()
}
}
for stmt in body {
scan_stmt_for_yield(
stmt, has_yield_ref, has_yield_from_ref, has_return_value_ref,
)
}
let is_generator = has_yield_ref.val || has_yield_from_ref.val
if is_async_def {
if has_yield_from_ref.val {
let mut idx = body_start_index
while idx < parser.lines.length() {
let body_line = parser.lines[idx]
let trimmed = body_line.trim(chars=" \t").to_owned()
if trimmed.length() == 0 || trimmed.has_prefix("#") {
idx += 1
continue
}
let body_indent = count_indent(body_line)
if body_indent < next_indent {
break
}
if trimmed.has_prefix("def ") ||
trimmed.has_prefix("async def ") ||
trimmed.has_prefix("class ") {
let def_indent = body_indent
idx += 1
while idx < parser.lines.length() {
let next_line = parser.lines[idx]
let next_trimmed = next_line.trim(chars=" \t").to_owned()
if next_trimmed.length() == 0 || next_trimmed.has_prefix("#") {
idx += 1
continue
}
if count_indent(next_line) <= def_indent {
break
}
idx += 1
}
continue
}
if trimmed.has_prefix("yield from") {
let token = {
kind: Eof,
start: parser.offsets[idx] + body_indent,
end: parser.offsets[idx] + body_indent + 1,
line: idx + 1,
column: body_indent + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"'yield from' inside async function".to_string(),
token,
),
)
}
idx += 1
}
}
if is_generator && has_return_value_ref.val {
let mut idx = body_start_index
while idx < parser.lines.length() {
let body_line = parser.lines[idx]
let trimmed = body_line.trim(chars=" \t").to_owned()
if trimmed.length() == 0 || trimmed.has_prefix("#") {
idx += 1
continue
}
let body_indent = count_indent(body_line)
if body_indent < next_indent {
break
}
if trimmed.has_prefix("def ") ||
trimmed.has_prefix("async def ") ||
trimmed.has_prefix("class ") {
let def_indent = body_indent
idx += 1
while idx < parser.lines.length() {
let next_line = parser.lines[idx]
let next_trimmed = next_line.trim(chars=" \t").to_owned()
if next_trimmed.length() == 0 || next_trimmed.has_prefix("#") {
idx += 1
continue
}
if count_indent(next_line) <= def_indent {
break
}
idx += 1
}
continue
}
if trimmed.has_prefix("return ") {
let token = {
kind: Eof,
start: parser.offsets[idx] + body_indent,
end: parser.offsets[idx] + body_indent + 1,
line: idx + 1,
column: body_indent + 1,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"'return' with value in async generator".to_string(),
token,
),
)
}
idx += 1
}
}
}
match scope_decl_after_use_line(body) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
match stmts_first_param_scope_decl_line(body, params) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
match stmts_first_function_import_star_line(body) {
Some(line) => return Err(parser.invalid_syntax_at_line(line))
None => ()
}
for stmt in body {
match stmt {
Stmt::Global(names) =>
for name in names {
for param in params {
match normalize_param_name_for_compare(param) {
Some(param_name) =>
if param_name == name {
let token = {
kind: Eof,
start: parser.offsets[parser.index - 1] + current_indent + 1,
end: parser.offsets[parser.index - 1] + current_indent + 2,
line: parser.index,
column: current_indent + 2,
}
return Err(
make_parse_error(
ParseErrorKind::Syntax,
"name '" + name + "' is parameter and global",
token,
),
)
}
None => ()
}
}
}
_ => ()
}
}
stmts.push(
wrap_stmt_for_line(
parser.offsets,
line_index,
current_indent,
Stmt::Function(
name~,
decorators~,
type_params~,
params~,
defaults~,
annotations~,
return_annotation~,
body~,
is_generator~,
is_async=is_async_def,
),
),
)
Ok((true, next_pending, next_token))
}