runner/env: centralize CLI/env getters; parser expr split (call/primary); verifier utils direct; optimizer: boxfield peephole; LLVM: branch cond normalize hook; add trace macro scaffolding; refactor common.rs verbose checks

This commit is contained in:
Selfhosting Dev
2025-09-17 06:55:39 +09:00
parent 9dc5c9afb9
commit c553f2952d
20 changed files with 651 additions and 677 deletions

40
src/parser/expr/bit.rs Normal file
View File

@ -0,0 +1,40 @@
use crate::parser::{NyashParser, ParseError};
use crate::parser::common::ParserUtils;
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, BinaryOperator, Span};
impl NyashParser {
pub(crate) fn expr_parse_bit_or(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.expr_parse_bit_xor()?;
while self.match_token(&TokenType::BitOr) {
let operator = BinaryOperator::BitOr;
self.advance();
let right = self.expr_parse_bit_xor()?;
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
pub(crate) fn expr_parse_bit_xor(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.expr_parse_bit_and()?;
while self.match_token(&TokenType::BitXor) {
let operator = BinaryOperator::BitXor;
self.advance();
let right = self.expr_parse_bit_and()?;
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
pub(crate) fn expr_parse_bit_and(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.expr_parse_equality()?;
while self.match_token(&TokenType::BitAnd) {
let operator = BinaryOperator::BitAnd;
self.advance();
let right = self.expr_parse_equality()?;
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
}

136
src/parser/expr/call.rs Normal file
View File

@ -0,0 +1,136 @@
use crate::parser::{NyashParser, ParseError};
use crate::parser::common::ParserUtils;
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, Span};
use crate::must_advance;
#[inline]
fn is_sugar_enabled() -> bool { crate::parser::sugar_gate::is_enabled() }
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 '?.'
// ident then optional call
let name = match &self.current_token().token_type {
TokenType::IDENTIFIER(s) => { let v = s.clone(); self.advance(); v }
_ => {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "identifier after '?.'".to_string(), line });
}
};
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()?);
if self.match_token(&TokenType::COMMA) { self.advance(); }
}
self.consume(TokenType::RPAREN)?;
ASTNode::MethodCall { object: Box::new(expr.clone()), method: name, arguments, span: Span::unknown() }
} else {
// field access
ASTNode::FieldAccess { object: Box::new(expr.clone()), field: name, span: Span::unknown() }
};
// Wrap with peek: peek expr { null => null, else => access(expr) }
expr = ASTNode::PeekExpr {
scrutinee: Box::new(expr.clone()),
arms: vec![(crate::ast::LiteralValue::Null, ASTNode::Literal { value: crate::ast::LiteralValue::Null, span: Span::unknown() })],
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()?);
if self.match_token(&TokenType::COMMA) { self.advance(); }
}
self.consume(TokenType::RPAREN)?;
if let ASTNode::Variable { name, .. } = expr.clone() {
expr = ASTNode::FunctionCall { name, arguments, span: Span::unknown() };
} else {
expr = ASTNode::Call { callee: Box::new(expr), arguments, span: Span::unknown() };
}
} else if self.match_token(&TokenType::QUESTION) {
let nt = self.peek_token();
let is_ender = matches!(nt,
TokenType::NEWLINE | TokenType::EOF | TokenType::RPAREN | TokenType::COMMA | TokenType::RBRACE
);
if !is_ender { break; }
self.advance();
expr = ASTNode::QMarkPropagate { expression: Box::new(expr), span: Span::unknown() };
} else {
break;
}
}
Ok(expr)
}
}

View File

@ -0,0 +1,48 @@
use crate::parser::{NyashParser, ParseError};
use crate::parser::common::ParserUtils;
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, BinaryOperator, Span};
impl NyashParser {
pub(crate) fn expr_parse_equality(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.expr_parse_comparison()?;
while self.match_token(&TokenType::EQUALS) || self.match_token(&TokenType::NotEquals) {
let operator = match &self.current_token().token_type {
TokenType::EQUALS => BinaryOperator::Equal,
TokenType::NotEquals => BinaryOperator::NotEqual,
_ => unreachable!(),
};
self.advance();
let right = self.expr_parse_comparison()?;
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let name = match operator { BinaryOperator::Equal=>"eq", BinaryOperator::NotEqual=>"ne", _=>"cmp" };
let ok = crate::grammar::engine::get().syntax_is_allowed_binop(name);
if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", name); }
}
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
pub(crate) fn expr_parse_comparison(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.expr_parse_range()?;
while self.match_token(&TokenType::LESS)
|| self.match_token(&TokenType::LessEquals)
|| self.match_token(&TokenType::GREATER)
|| self.match_token(&TokenType::GreaterEquals)
{
let operator = match &self.current_token().token_type {
TokenType::LESS => BinaryOperator::Less,
TokenType::LessEquals => BinaryOperator::LessEqual,
TokenType::GREATER => BinaryOperator::Greater,
TokenType::GreaterEquals => BinaryOperator::GreaterEqual,
_ => unreachable!(),
};
self.advance();
let right = self.expr_parse_range()?;
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
}

31
src/parser/expr/factor.rs Normal file
View File

@ -0,0 +1,31 @@
use crate::parser::{NyashParser, ParseError};
use crate::parser::common::ParserUtils;
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, BinaryOperator, Span};
impl NyashParser {
pub(crate) fn expr_parse_factor(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_unary()?;
while self.match_token(&TokenType::MULTIPLY)
|| self.match_token(&TokenType::DIVIDE)
|| self.match_token(&TokenType::MODULO)
{
let operator = match &self.current_token().token_type {
TokenType::MULTIPLY => BinaryOperator::Multiply,
TokenType::DIVIDE => BinaryOperator::Divide,
TokenType::MODULO => BinaryOperator::Modulo,
_ => unreachable!(),
};
self.advance();
let right = self.parse_unary()?;
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let name = match operator { BinaryOperator::Multiply=>"mul", BinaryOperator::Divide=>"div", _=>"mod" };
let ok = crate::grammar::engine::get().syntax_is_allowed_binop(name);
if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", name); }
}
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
}

View File

@ -20,11 +20,11 @@ impl NyashParser {
}
pub(crate) fn expr_parse_and(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_bit_or()?;
let mut expr = self.expr_parse_bit_or()?;
while self.match_token(&TokenType::AND) {
let operator = BinaryOperator::And;
self.advance();
let right = self.parse_equality()?;
let right = self.expr_parse_equality()?;
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let ok = crate::grammar::engine::get().syntax_is_allowed_binop("and");
if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop 'and' not allowed by syntax rules"); }

View File

@ -1,4 +1,11 @@
pub(crate) mod ternary;
pub(crate) mod coalesce;
pub(crate) mod logic;
pub(crate) mod bit;
pub(crate) mod compare;
pub(crate) mod range;
pub(crate) mod term;
pub(crate) mod shift;
pub(crate) mod factor;
pub(crate) mod call;
pub(crate) mod primary;

152
src/parser/expr/primary.rs Normal file
View File

@ -0,0 +1,152 @@
use crate::parser::{NyashParser, ParseError};
use crate::parser::common::ParserUtils;
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, Span, LiteralValue};
impl NyashParser {
pub(crate) fn expr_parse_primary(&mut self) -> Result<ASTNode, ParseError> {
match &self.current_token().token_type {
TokenType::LBRACK => {
let sugar_on = crate::parser::sugar_gate::is_enabled()
|| std::env::var("NYASH_ENABLE_ARRAY_LITERAL").ok().as_deref() == Some("1");
if !sugar_on {
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 or NYASH_ENABLE_ARRAY_LITERAL=1".to_string(),
line,
});
}
self.advance();
let mut elems: Vec<ASTNode> = Vec::new();
while !self.match_token(&TokenType::RBRACK) && !self.is_at_end() {
crate::must_advance!(self, _unused, "array literal element parsing");
let el = self.parse_expression()?;
elems.push(el);
if self.match_token(&TokenType::COMMA) { self.advance(); }
}
self.consume(TokenType::RBRACK)?;
Ok(ASTNode::ArrayLiteral { elements: elems, span: Span::unknown() })
}
TokenType::LBRACE => {
let sugar_on = crate::parser::sugar_gate::is_enabled()
|| std::env::var("NYASH_ENABLE_MAP_LITERAL").ok().as_deref() == Some("1");
if !sugar_on {
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 or NYASH_ENABLE_MAP_LITERAL=1".to_string(), line });
}
self.advance();
let mut entries: Vec<(String, ASTNode)> = Vec::new();
let sugar_level = std::env::var("NYASH_SYNTAX_SUGAR_LEVEL").ok();
let ident_key_on = std::env::var("NYASH_ENABLE_MAP_IDENT_KEY").ok().as_deref() == Some("1") || sugar_level.as_deref() == Some("full");
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
let key = match &self.current_token().token_type {
TokenType::STRING(s) => { let v = s.clone(); self.advance(); v }
TokenType::IDENTIFIER(id) if ident_key_on => { let v = id.clone(); self.advance(); v }
_ => {
let line = self.current_token().line;
return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: if ident_key_on { "string or identifier key in map literal".to_string() } else { "string key in map literal".to_string() }, line });
}
};
self.consume(TokenType::COLON)?;
let value_expr = self.parse_expression()?;
entries.push((key, value_expr));
if self.match_token(&TokenType::COMMA) { self.advance(); }
}
self.consume(TokenType::RBRACE)?;
Ok(ASTNode::MapLiteral { entries, span: Span::unknown() })
}
TokenType::INCLUDE => { self.parse_include() }
TokenType::STRING(s) => { let value = s.clone(); self.advance(); Ok(ASTNode::Literal { value: LiteralValue::String(value), span: Span::unknown() }) }
TokenType::NUMBER(n) => { let value = *n; self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Integer(value), span: Span::unknown() }) }
TokenType::FLOAT(f) => { let value = *f; self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Float(value), span: Span::unknown() }) }
TokenType::TRUE => { self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown() }) }
TokenType::FALSE => { self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Bool(false), span: Span::unknown() }) }
TokenType::NULL => { self.advance(); Ok(ASTNode::Literal { value: LiteralValue::Null, span: Span::unknown() }) }
TokenType::THIS => {
if std::env::var("NYASH_DEPRECATE_THIS").ok().as_deref() == Some("1") {
eprintln!("[deprecate:this] 'this' is deprecated; use 'me' instead (line {})", self.current_token().line);
}
self.advance();
Ok(ASTNode::Me { span: Span::unknown() })
}
TokenType::ME => { self.advance(); Ok(ASTNode::Me { span: Span::unknown() }) }
TokenType::NEW => {
self.advance();
if let TokenType::IDENTIFIER(class_name) = &self.current_token().token_type {
let class = class_name.clone();
self.advance();
let mut type_arguments: Vec<String> = Vec::new();
if self.match_token(&TokenType::LESS) {
self.advance();
loop {
if let TokenType::IDENTIFIER(tn) = &self.current_token().token_type { type_arguments.push(tn.clone()); self.advance(); }
else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "type argument".to_string(), line }); }
if self.match_token(&TokenType::COMMA) { self.advance(); continue; }
self.consume(TokenType::GREATER)?; break;
}
}
self.consume(TokenType::LPAREN)?;
let mut arguments = Vec::new();
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
crate::must_advance!(self, _unused, "new expression argument parsing");
arguments.push(self.parse_expression()?);
if self.match_token(&TokenType::COMMA) { self.advance(); }
}
self.consume(TokenType::RPAREN)?;
Ok(ASTNode::New { class, arguments, type_arguments, span: Span::unknown() })
} else {
let line = self.current_token().line;
Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "class name".to_string(), line })
}
}
TokenType::FROM => { self.parse_from_call() }
TokenType::IDENTIFIER(name) => {
let parent = name.clone();
self.advance();
if self.match_token(&TokenType::DoubleColon) {
self.advance();
let method = match &self.current_token().token_type {
TokenType::IDENTIFIER(m) => { let s=m.clone(); self.advance(); s }
TokenType::INIT => { self.advance(); "init".to_string() }
TokenType::PACK => { self.advance(); "pack".to_string() }
TokenType::BIRTH => { self.advance(); "birth".to_string() }
_ => { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "method name".to_string(), line }); }
};
self.consume(TokenType::LPAREN)?;
let mut arguments = Vec::new();
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
crate::must_advance!(self, _unused, "Parent::method call argument parsing");
arguments.push(self.parse_expression()?);
if self.match_token(&TokenType::COMMA) { self.advance(); }
}
self.consume(TokenType::RPAREN)?;
Ok(ASTNode::FromCall { parent, method, arguments, span: Span::unknown() })
} else {
Ok(ASTNode::Variable { name: parent, span: Span::unknown() })
}
}
TokenType::LPAREN => { self.advance(); let expr = self.parse_expression()?; self.consume(TokenType::RPAREN)?; Ok(expr) }
TokenType::FN => {
self.advance();
let mut params: Vec<String> = Vec::new();
if self.match_token(&TokenType::LPAREN) { self.advance();
while !self.match_token(&TokenType::RPAREN) && !self.is_at_end() {
if let TokenType::IDENTIFIER(p) = &self.current_token().token_type { params.push(p.clone()); self.advance(); if self.match_token(&TokenType::COMMA) { self.advance(); } }
else { let line = self.current_token().line; return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "parameter name".to_string(), line }); }
}
self.consume(TokenType::RPAREN)?;
}
self.consume(TokenType::LBRACE)?;
let mut body: Vec<ASTNode> = Vec::new();
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
self.skip_newlines();
if !self.match_token(&TokenType::RBRACE) { body.push(self.parse_statement()?); }
}
self.consume(TokenType::RBRACE)?;
Ok(ASTNode::Lambda { params, body, span: Span::unknown() })
}
_ => { let line = self.current_token().line; Err(ParseError::InvalidExpression { line }) }
}
}
}

28
src/parser/expr/range.rs Normal file
View File

@ -0,0 +1,28 @@
use crate::parser::{NyashParser, ParseError};
use crate::parser::common::ParserUtils;
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, Span};
#[inline]
fn is_sugar_enabled() -> bool { crate::parser::sugar_gate::is_enabled() }
impl NyashParser {
pub(crate) fn expr_parse_range(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.expr_parse_term()?;
while self.match_token(&TokenType::RANGE) {
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();
let rhs = self.expr_parse_term()?;
expr = ASTNode::FunctionCall { name: "Range".to_string(), arguments: vec![expr, rhs], span: Span::unknown() };
}
Ok(expr)
}
}

27
src/parser/expr/shift.rs Normal file
View File

@ -0,0 +1,27 @@
use crate::parser::{NyashParser, ParseError};
use crate::parser::common::ParserUtils;
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, BinaryOperator, Span};
impl NyashParser {
pub(crate) fn expr_parse_shift(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.expr_parse_factor()?;
loop {
if self.match_token(&TokenType::ShiftLeft) {
self.advance();
let rhs = self.expr_parse_factor()?;
expr = ASTNode::BinaryOp { operator: BinaryOperator::Shl, left: Box::new(expr), right: Box::new(rhs), span: Span::unknown() };
continue;
}
if self.match_token(&TokenType::ShiftRight) {
self.advance();
let rhs = self.expr_parse_factor()?;
expr = ASTNode::BinaryOp { operator: BinaryOperator::Shr, left: Box::new(expr), right: Box::new(rhs), span: Span::unknown() };
continue;
}
break;
}
Ok(expr)
}
}

27
src/parser/expr/term.rs Normal file
View File

@ -0,0 +1,27 @@
use crate::parser::{NyashParser, ParseError};
use crate::parser::common::ParserUtils;
use crate::tokenizer::TokenType;
use crate::ast::{ASTNode, BinaryOperator, Span};
impl NyashParser {
pub(crate) fn expr_parse_term(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.expr_parse_shift()?;
while self.match_token(&TokenType::PLUS) || self.match_token(&TokenType::MINUS) {
let operator = match &self.current_token().token_type {
TokenType::PLUS => BinaryOperator::Add,
TokenType::MINUS => BinaryOperator::Subtract,
_ => unreachable!(),
};
self.advance();
let right = self.expr_parse_shift()?;
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let name = match operator { BinaryOperator::Add=>"add", BinaryOperator::Subtract=>"sub", _=>"term" };
let ok = crate::grammar::engine::get().syntax_is_allowed_binop(name);
if !ok { eprintln!("[GRAMMAR-DIFF][Parser] binop '{}' not allowed by syntax rules", name); }
}
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
}