2025-09-17 06:55:39 +09:00
|
|
|
use crate::ast::{ASTNode, Span};
|
|
|
|
|
use crate::must_advance;
|
2025-09-17 07:43:07 +09:00
|
|
|
use crate::parser::common::ParserUtils;
|
|
|
|
|
use crate::parser::{NyashParser, ParseError};
|
|
|
|
|
use crate::tokenizer::TokenType;
|
2025-09-17 06:55:39 +09:00
|
|
|
|
|
|
|
|
#[inline]
|
2025-09-17 07:43:07 +09:00
|
|
|
fn is_sugar_enabled() -> bool {
|
|
|
|
|
crate::parser::sugar_gate::is_enabled()
|
|
|
|
|
}
|
2025-09-17 06:55:39 +09:00
|
|
|
|
|
|
|
|
impl NyashParser {
|
|
|
|
|
pub(crate) fn expr_parse_call(&mut self) -> Result<ASTNode, ParseError> {
|
|
|
|
|
let mut expr = self.expr_parse_primary()?;
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
if self.match_token(&TokenType::DOT) {
|
|
|
|
|
self.advance(); // consume '.'
|
|
|
|
|
|
|
|
|
|
if let TokenType::IDENTIFIER(method_name) = &self.current_token().token_type {
|
|
|
|
|
let method_name = method_name.clone();
|
|
|
|
|
self.advance();
|
|
|
|
|
|
|
|
|
|
if self.match_token(&TokenType::LPAREN) {
|
|
|
|
|
// メソッド呼び出し: obj.method(args)
|
|
|
|
|
self.advance(); // consume '('
|
|
|
|
|
let mut arguments = Vec::new();
|
|
|
|
|
let mut _arg_count = 0;
|
|
|
|
|
|
|
|
|
|
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
|
|
|
|
|
must_advance!(self, _unused, "method call argument parsing");
|
|
|
|
|
|
|
|
|
|
arguments.push(self.parse_expression()?);
|
|
|
|
|
_arg_count += 1;
|
|
|
|
|
|
|
|
|
|
if self.match_token(&TokenType::COMMA) {
|
|
|
|
|
self.advance();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.consume(TokenType::RPAREN)?;
|
|
|
|
|
|
|
|
|
|
expr = ASTNode::MethodCall {
|
|
|
|
|
object: Box::new(expr),
|
|
|
|
|
method: method_name,
|
|
|
|
|
arguments,
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
// フィールドアクセス: obj.field
|
|
|
|
|
expr = ASTNode::FieldAccess {
|
|
|
|
|
object: Box::new(expr),
|
|
|
|
|
field: method_name,
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
let line = self.current_token().line;
|
|
|
|
|
return Err(ParseError::UnexpectedToken {
|
|
|
|
|
found: self.current_token().token_type.clone(),
|
|
|
|
|
expected: "identifier".to_string(),
|
|
|
|
|
line,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else if self.match_token(&TokenType::QmarkDot) {
|
|
|
|
|
if !is_sugar_enabled() {
|
|
|
|
|
let line = self.current_token().line;
|
|
|
|
|
return Err(ParseError::UnexpectedToken {
|
|
|
|
|
found: self.current_token().token_type.clone(),
|
|
|
|
|
expected: "enable NYASH_SYNTAX_SUGAR_LEVEL=basic|full for '?.'".to_string(),
|
|
|
|
|
line,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
self.advance(); // consume '?.'
|
2025-09-17 07:43:07 +09:00
|
|
|
// ident then optional call
|
2025-09-17 06:55:39 +09:00
|
|
|
let name = match &self.current_token().token_type {
|
2025-09-17 07:43:07 +09:00
|
|
|
TokenType::IDENTIFIER(s) => {
|
|
|
|
|
let v = s.clone();
|
|
|
|
|
self.advance();
|
|
|
|
|
v
|
|
|
|
|
}
|
2025-09-17 06:55:39 +09:00
|
|
|
_ => {
|
|
|
|
|
let line = self.current_token().line;
|
2025-09-17 07:43:07 +09:00
|
|
|
return Err(ParseError::UnexpectedToken {
|
|
|
|
|
found: self.current_token().token_type.clone(),
|
|
|
|
|
expected: "identifier after '?.'".to_string(),
|
|
|
|
|
line,
|
|
|
|
|
});
|
2025-09-17 06:55:39 +09:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let access = if self.match_token(&TokenType::LPAREN) {
|
|
|
|
|
// method call
|
|
|
|
|
self.advance();
|
|
|
|
|
let mut arguments = Vec::new();
|
|
|
|
|
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
|
|
|
|
|
must_advance!(self, _unused, "safe method call arg parsing");
|
|
|
|
|
arguments.push(self.parse_expression()?);
|
2025-09-17 07:43:07 +09:00
|
|
|
if self.match_token(&TokenType::COMMA) {
|
|
|
|
|
self.advance();
|
|
|
|
|
}
|
2025-09-17 06:55:39 +09:00
|
|
|
}
|
|
|
|
|
self.consume(TokenType::RPAREN)?;
|
2025-09-17 07:43:07 +09:00
|
|
|
ASTNode::MethodCall {
|
|
|
|
|
object: Box::new(expr.clone()),
|
|
|
|
|
method: name,
|
|
|
|
|
arguments,
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
}
|
2025-09-17 06:55:39 +09:00
|
|
|
} else {
|
|
|
|
|
// field access
|
2025-09-17 07:43:07 +09:00
|
|
|
ASTNode::FieldAccess {
|
|
|
|
|
object: Box::new(expr.clone()),
|
|
|
|
|
field: name,
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
}
|
2025-09-17 06:55:39 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Wrap with peek: peek expr { null => null, else => access(expr) }
|
|
|
|
|
expr = ASTNode::PeekExpr {
|
|
|
|
|
scrutinee: Box::new(expr.clone()),
|
2025-09-17 07:43:07 +09:00
|
|
|
arms: vec![(
|
|
|
|
|
crate::ast::LiteralValue::Null,
|
|
|
|
|
ASTNode::Literal {
|
|
|
|
|
value: crate::ast::LiteralValue::Null,
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
},
|
|
|
|
|
)],
|
2025-09-17 06:55:39 +09:00
|
|
|
else_expr: Box::new(access),
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
};
|
|
|
|
|
} else if self.match_token(&TokenType::LPAREN) {
|
|
|
|
|
// 関数呼び出し: function(args) または 一般式呼び出し: (callee)(args)
|
|
|
|
|
self.advance(); // consume '('
|
|
|
|
|
let mut arguments = Vec::new();
|
|
|
|
|
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
|
|
|
|
|
must_advance!(self, _unused, "function call argument parsing");
|
|
|
|
|
arguments.push(self.parse_expression()?);
|
2025-09-17 07:43:07 +09:00
|
|
|
if self.match_token(&TokenType::COMMA) {
|
|
|
|
|
self.advance();
|
|
|
|
|
}
|
2025-09-17 06:55:39 +09:00
|
|
|
}
|
|
|
|
|
self.consume(TokenType::RPAREN)?;
|
|
|
|
|
|
|
|
|
|
if let ASTNode::Variable { name, .. } = expr.clone() {
|
2025-09-17 07:43:07 +09:00
|
|
|
expr = ASTNode::FunctionCall {
|
|
|
|
|
name,
|
|
|
|
|
arguments,
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
};
|
2025-09-17 06:55:39 +09:00
|
|
|
} else {
|
2025-09-17 07:43:07 +09:00
|
|
|
expr = ASTNode::Call {
|
|
|
|
|
callee: Box::new(expr),
|
|
|
|
|
arguments,
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
};
|
2025-09-17 06:55:39 +09:00
|
|
|
}
|
|
|
|
|
} else if self.match_token(&TokenType::QUESTION) {
|
|
|
|
|
let nt = self.peek_token();
|
2025-09-17 07:43:07 +09:00
|
|
|
let is_ender = matches!(
|
|
|
|
|
nt,
|
|
|
|
|
TokenType::NEWLINE
|
|
|
|
|
| TokenType::EOF
|
|
|
|
|
| TokenType::RPAREN
|
|
|
|
|
| TokenType::COMMA
|
|
|
|
|
| TokenType::RBRACE
|
2025-09-17 06:55:39 +09:00
|
|
|
);
|
2025-09-17 07:43:07 +09:00
|
|
|
if !is_ender {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-09-17 06:55:39 +09:00
|
|
|
self.advance();
|
2025-09-17 07:43:07 +09:00
|
|
|
expr = ASTNode::QMarkPropagate {
|
|
|
|
|
expression: Box::new(expr),
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
};
|
2025-09-17 06:55:39 +09:00
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(expr)
|
|
|
|
|
}
|
|
|
|
|
}
|