resolve: apply stashed using/module + deps bridge; remove conflict markers in runner/mod.rs
This commit is contained in:
12
src/ast.rs
12
src/ast.rs
@ -443,6 +443,12 @@ pub enum ASTNode {
|
||||
namespace_name: String,
|
||||
span: Span,
|
||||
},
|
||||
/// import文: import "path" (as Alias)?
|
||||
ImportStatement {
|
||||
path: String,
|
||||
alias: Option<String>,
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// nowait文: nowait variable = expression
|
||||
Nowait {
|
||||
@ -673,6 +679,7 @@ impl ASTNode {
|
||||
ASTNode::Break { .. } => "Break",
|
||||
ASTNode::Continue { .. } => "Continue",
|
||||
ASTNode::UsingStatement { .. } => "UsingStatement",
|
||||
ASTNode::ImportStatement { .. } => "ImportStatement",
|
||||
ASTNode::BoxDeclaration { .. } => "BoxDeclaration",
|
||||
ASTNode::FunctionDeclaration { .. } => "FunctionDeclaration",
|
||||
ASTNode::GlobalVar { .. } => "GlobalVar",
|
||||
@ -742,6 +749,7 @@ impl ASTNode {
|
||||
ASTNode::Break { .. } => ASTNodeType::Statement,
|
||||
ASTNode::Continue { .. } => ASTNodeType::Statement,
|
||||
ASTNode::UsingStatement { .. } => ASTNodeType::Statement,
|
||||
ASTNode::ImportStatement { .. } => ASTNodeType::Statement,
|
||||
ASTNode::GlobalVar { .. } => ASTNodeType::Statement,
|
||||
ASTNode::Include { .. } => ASTNodeType::Statement,
|
||||
ASTNode::Local { .. } => ASTNodeType::Statement,
|
||||
@ -794,6 +802,9 @@ impl ASTNode {
|
||||
ASTNode::UsingStatement { namespace_name, .. } => {
|
||||
format!("UsingStatement({})", namespace_name)
|
||||
}
|
||||
ASTNode::ImportStatement { path, alias, .. } => {
|
||||
if let Some(a) = alias { format!("ImportStatement({}, as {})", path, a) } else { format!("ImportStatement({})", path) }
|
||||
}
|
||||
ASTNode::BoxDeclaration { name, fields, methods, constructors, is_interface, extends, implements, .. } => {
|
||||
let mut desc = if *is_interface {
|
||||
format!("InterfaceBox({}, {} methods", name, methods.len())
|
||||
@ -907,6 +918,7 @@ impl ASTNode {
|
||||
ASTNode::Break { span, .. } => *span,
|
||||
ASTNode::Continue { span, .. } => *span,
|
||||
ASTNode::UsingStatement { span, .. } => *span,
|
||||
ASTNode::ImportStatement { span, .. } => *span,
|
||||
ASTNode::Nowait { span, .. } => *span,
|
||||
ASTNode::Arrow { span, .. } => *span,
|
||||
ASTNode::TryCatch { span, .. } => *span,
|
||||
|
||||
@ -50,6 +50,7 @@ pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[
|
||||
"print",
|
||||
"nowait",
|
||||
"include",
|
||||
"import",
|
||||
"local",
|
||||
"outbox",
|
||||
"try",
|
||||
@ -66,4 +67,4 @@ pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &[
|
||||
"or",
|
||||
"eq",
|
||||
"ne",
|
||||
];
|
||||
];
|
||||
|
||||
@ -100,6 +100,14 @@ impl NyashInterpreter {
|
||||
self.execute_using_statement(namespace_name)
|
||||
}
|
||||
|
||||
ASTNode::ImportStatement { path, alias, .. } => {
|
||||
// Stage-0 import: no-op (record/log only)
|
||||
if std::env::var("NYASH_IMPORT_TRACE").ok().as_deref() == Some("1") {
|
||||
if let Some(a) = alias { eprintln!("[import] {} as {}", path, a); } else { eprintln!("[import] {}", path); }
|
||||
}
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, is_static, static_init, .. } => {
|
||||
if *is_static {
|
||||
// 🔥 Static Box宣言の処理
|
||||
@ -613,19 +621,25 @@ impl NyashInterpreter {
|
||||
pub(super) fn execute_using_statement(&mut self, namespace_name: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
||||
idebug!("🌟 DEBUG: execute_using_statement called with namespace: {}", namespace_name);
|
||||
|
||||
// Phase 0: nyashstdのみサポート
|
||||
if namespace_name != "nyashstd" {
|
||||
return Err(RuntimeError::InvalidOperation {
|
||||
message: format!("Unsupported namespace '{}'. Only 'nyashstd' is supported in Phase 0.", namespace_name)
|
||||
});
|
||||
// First, handle the builtin stdlib namespace
|
||||
if namespace_name == "nyashstd" {
|
||||
idebug!("🌟 DEBUG: About to call ensure_stdlib_initialized");
|
||||
self.ensure_stdlib_initialized()?;
|
||||
idebug!("🌟 DEBUG: ensure_stdlib_initialized completed");
|
||||
return Ok(Box::new(VoidBox::new()));
|
||||
}
|
||||
// Otherwise, consult the modules registry (resolved by runner/CLI/header)
|
||||
if crate::runtime::modules_registry::get(namespace_name).is_some() {
|
||||
// Resolved via registry; no further action at runtime stage-0
|
||||
return Ok(Box::new(VoidBox::new()));
|
||||
}
|
||||
let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1");
|
||||
if strict {
|
||||
return Err(RuntimeError::InvalidOperation { message: format!("Unresolved namespace '{}' (strict)", namespace_name) });
|
||||
}
|
||||
if crate::interpreter::utils::debug_on() {
|
||||
eprintln!("[using] unresolved '{}' (non-strict, continuing)", namespace_name);
|
||||
}
|
||||
|
||||
// 標準ライブラリを初期化(存在しない場合)
|
||||
idebug!("🌟 DEBUG: About to call ensure_stdlib_initialized");
|
||||
self.ensure_stdlib_initialized()?;
|
||||
idebug!("🌟 DEBUG: ensure_stdlib_initialized completed");
|
||||
|
||||
// using nyashstdの場合は特に何もしない(既に標準ライブラリが初期化されている)
|
||||
Ok(Box::new(VoidBox::new()))
|
||||
}
|
||||
|
||||
|
||||
@ -407,33 +407,58 @@ impl NyashParser {
|
||||
let field_or_method = field_or_method.clone();
|
||||
self.advance();
|
||||
|
||||
// 可視性ブロック: public { ... } / private { ... }
|
||||
// 可視性:
|
||||
// - public { ... } / private { ... } ブロック
|
||||
// - public name: Type 単行(P0: 型はパースのみ、意味付けは後段)
|
||||
if field_or_method == "public" || field_or_method == "private" {
|
||||
self.consume(TokenType::LBRACE)?;
|
||||
self.skip_newlines();
|
||||
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
||||
if let TokenType::IDENTIFIER(fname) = &self.current_token().token_type {
|
||||
let fname = fname.clone();
|
||||
// ブロックに追加
|
||||
if field_or_method == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); }
|
||||
// 互換性のため、全体fieldsにも追加
|
||||
fields.push(fname);
|
||||
self.advance();
|
||||
// カンマ/改行をスキップ
|
||||
if self.match_token(&TokenType::COMMA) { self.advance(); }
|
||||
self.skip_newlines();
|
||||
continue;
|
||||
if self.match_token(&TokenType::LBRACE) {
|
||||
// ブロック形式
|
||||
self.advance(); // consume '{'
|
||||
self.skip_newlines();
|
||||
while !self.match_token(&TokenType::RBRACE) && !self.is_at_end() {
|
||||
if let TokenType::IDENTIFIER(fname) = &self.current_token().token_type {
|
||||
let fname = fname.clone();
|
||||
// ブロックに追加
|
||||
if field_or_method == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); }
|
||||
// 互換性のため、全体fieldsにも追加
|
||||
fields.push(fname);
|
||||
self.advance();
|
||||
// カンマ/改行をスキップ
|
||||
if self.match_token(&TokenType::COMMA) { self.advance(); }
|
||||
self.skip_newlines();
|
||||
continue;
|
||||
}
|
||||
// 予期しないトークン
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
expected: "identifier in visibility block".to_string(),
|
||||
found: self.current_token().token_type.clone(),
|
||||
line: self.current_token().line,
|
||||
});
|
||||
}
|
||||
// 予期しないトークン
|
||||
return Err(ParseError::UnexpectedToken {
|
||||
expected: "identifier in visibility block".to_string(),
|
||||
found: self.current_token().token_type.clone(),
|
||||
line: self.current_token().line,
|
||||
});
|
||||
self.consume(TokenType::RBRACE)?;
|
||||
self.skip_newlines();
|
||||
continue;
|
||||
} else if self.match_token(&TokenType::IDENTIFIER) {
|
||||
// 単行形式: public name[: Type]
|
||||
let fname = if let TokenType::IDENTIFIER(n) = &self.current_token().token_type { n.clone() } else { unreachable!() };
|
||||
self.advance();
|
||||
if self.match_token(&TokenType::COLON) {
|
||||
self.advance(); // consume ':'
|
||||
// 型名(識別子)を受理して破棄(P0)
|
||||
if let TokenType::IDENTIFIER(_ty) = &self.current_token().token_type {
|
||||
self.advance();
|
||||
} else {
|
||||
return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "type name".to_string(), line: self.current_token().line });
|
||||
}
|
||||
}
|
||||
if field_or_method == "public" { public_fields.push(fname.clone()); } else { private_fields.push(fname.clone()); }
|
||||
fields.push(fname);
|
||||
self.skip_newlines();
|
||||
continue;
|
||||
} else {
|
||||
// public/private の後に '{' でも識別子でもない
|
||||
return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "'{' or field name".to_string(), line: self.current_token().line });
|
||||
}
|
||||
self.consume(TokenType::RBRACE)?;
|
||||
self.skip_newlines();
|
||||
continue;
|
||||
}
|
||||
|
||||
// メソッドかフィールドかを判定
|
||||
|
||||
@ -19,6 +19,9 @@ impl NyashParser {
|
||||
TokenType::BOX => {
|
||||
self.parse_box_declaration()
|
||||
},
|
||||
TokenType::IMPORT => {
|
||||
self.parse_import()
|
||||
},
|
||||
TokenType::INTERFACE => {
|
||||
self.parse_interface_box_declaration()
|
||||
},
|
||||
@ -117,6 +120,32 @@ impl NyashParser {
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// import文をパース: import "path" (as Alias)?
|
||||
pub(super) fn parse_import(&mut self) -> Result<ASTNode, ParseError> {
|
||||
self.advance(); // consume 'import'
|
||||
let path = if let TokenType::STRING(s) = &self.current_token().token_type {
|
||||
let v = s.clone();
|
||||
self.advance();
|
||||
v
|
||||
} else {
|
||||
return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "string literal".to_string(), line: self.current_token().line });
|
||||
};
|
||||
// Optional: 'as' Alias (treat 'as' as identifier literal)
|
||||
let mut alias: Option<String> = None;
|
||||
if let TokenType::IDENTIFIER(w) = &self.current_token().token_type {
|
||||
if w == "as" {
|
||||
self.advance();
|
||||
if let TokenType::IDENTIFIER(name) = &self.current_token().token_type {
|
||||
alias = Some(name.clone());
|
||||
self.advance();
|
||||
} else {
|
||||
return Err(ParseError::UnexpectedToken { found: self.current_token().token_type.clone(), expected: "alias name".to_string(), line: self.current_token().line });
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ASTNode::ImportStatement { path, alias, span: Span::unknown() })
|
||||
}
|
||||
|
||||
/// if文をパース: if (condition) { body } else if ... else { body }
|
||||
pub(super) fn parse_if(&mut self) -> Result<ASTNode, ParseError> {
|
||||
|
||||
@ -36,6 +36,38 @@ use nyash_rust::runtime;
|
||||
use nyash_rust::runner_plugin_init;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Resolve a using target according to priority: modules > relative > using-paths
|
||||
/// Returns Ok(resolved_path_or_token). On strict mode, ambiguous matches cause error.
|
||||
fn resolve_using_target(
|
||||
tgt: &str,
|
||||
is_path: bool,
|
||||
modules: &[(String, String)],
|
||||
using_paths: &[String],
|
||||
context_dir: Option<&std::path::Path>,
|
||||
strict: bool,
|
||||
verbose: bool,
|
||||
) -> Result<String, String> {
|
||||
if is_path { return Ok(tgt.to_string()); }
|
||||
// 1) modules mapping
|
||||
if let Some((_, p)) = modules.iter().find(|(n, _)| n == tgt) { return Ok(p.clone()); }
|
||||
// 2) build candidate list: relative then using-paths
|
||||
let rel = tgt.replace('.', "/") + ".nyash";
|
||||
let mut cand: Vec<String> = Vec::new();
|
||||
if let Some(dir) = context_dir { let c = dir.join(&rel); if c.exists() { cand.push(c.to_string_lossy().to_string()); } }
|
||||
for base in using_paths {
|
||||
let c = std::path::Path::new(base).join(&rel);
|
||||
if c.exists() { cand.push(c.to_string_lossy().to_string()); }
|
||||
}
|
||||
if cand.is_empty() {
|
||||
if verbose { eprintln!("[using] unresolved '{}' (searched: rel+paths)", tgt); }
|
||||
return Ok(tgt.to_string());
|
||||
}
|
||||
if cand.len() > 1 && strict {
|
||||
return Err(format!("ambiguous using '{}': {}", tgt, cand.join(", ")));
|
||||
}
|
||||
Ok(cand.remove(0))
|
||||
}
|
||||
|
||||
/// Main execution coordinator
|
||||
pub struct NyashRunner {
|
||||
config: CliConfig,
|
||||
@ -83,6 +115,66 @@ impl NyashRunner {
|
||||
}
|
||||
return;
|
||||
}
|
||||
// CLI using/module overrides (MVP): apply early so JSON pipeline can observe them
|
||||
if self.config.using.is_some() || self.config.using_path.is_some() || self.config.modules.is_some()
|
||||
|| std::env::var("NYASH_USING_PATH").is_ok() || std::env::var("NYASH_MODULES").is_ok() {
|
||||
let mut using_paths: Vec<String> = Vec::new();
|
||||
if let Some(p) = self.config.using_path.clone() { for s in p.split(':') { let s=s.trim(); if !s.is_empty() { using_paths.push(s.to_string()); } } }
|
||||
if let Ok(p) = std::env::var("NYASH_USING_PATH") { for s in p.split(':') { let s=s.trim(); if !s.is_empty() { using_paths.push(s.to_string()); } } }
|
||||
if using_paths.is_empty() { using_paths.extend(["apps","lib","."].into_iter().map(|s| s.to_string())); }
|
||||
|
||||
// modules mapping
|
||||
let mut modules: Vec<(String,String)> = Vec::new();
|
||||
if let Some(m) = self.config.modules.clone() { for ent in m.split(',') { if let Some((k,v)) = ent.split_once('=') { let k=k.trim(); let v=v.trim(); if !k.is_empty() && !v.is_empty() { modules.push((k.to_string(), v.to_string())); } } } }
|
||||
if let Ok(ms) = std::env::var("NYASH_MODULES") { for ent in ms.split(',') { if let Some((k,v)) = ent.split_once('=') { let k=k.trim(); let v=v.trim(); if !k.is_empty() && !v.is_empty() { modules.push((k.to_string(), v.to_string())); } } } }
|
||||
for (ns, path) in &modules { let sb = crate::box_trait::StringBox::new(path.clone()); crate::runtime::modules_registry::set(ns.clone(), Box::new(sb)); }
|
||||
|
||||
// using specs
|
||||
let mut pending_using: Vec<(String, Option<String>, bool)> = Vec::new(); // (target, alias, is_path)
|
||||
if let Some(u) = self.config.using.clone() {
|
||||
for ent in u.split(',') {
|
||||
let s = ent.trim().trim_end_matches(';').trim(); if s.is_empty() { continue; }
|
||||
let (tgt, alias) = if let Some(pos) = s.find(" as ") { (s[..pos].trim().to_string(), Some(s[pos+4..].trim().to_string())) } else { (s.to_string(), None) };
|
||||
let is_path = tgt.starts_with('"') || tgt.starts_with("./") || tgt.starts_with('/') || tgt.ends_with(".nyash");
|
||||
pending_using.push((tgt.trim_matches('"').to_string(), alias, is_path));
|
||||
}
|
||||
}
|
||||
// Resolve using (priority: modules > relative(file) > using-paths; ambiguous=error if strict)
|
||||
let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1");
|
||||
let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1");
|
||||
let ctx = self.config.file.as_deref().and_then(|f| std::path::Path::new(f).parent());
|
||||
for (tgt, alias, is_path) in pending_using.into_iter() {
|
||||
if is_path && !std::path::Path::new(&tgt).exists() {
|
||||
if strict { eprintln!("❌ using: path not found: {}", tgt); std::process::exit(1); }
|
||||
if verbose { eprintln!("[using] path not found (continuing): {}", tgt); }
|
||||
}
|
||||
let value = match resolve_using_target(&tgt, is_path, &modules, &using_paths, ctx, strict, verbose) {
|
||||
Ok(v) => v,
|
||||
Err(e) => { eprintln!("❌ using: {}", e); std::process::exit(1); }
|
||||
};
|
||||
let sb = crate::box_trait::StringBox::new(value.clone());
|
||||
crate::runtime::modules_registry::set(tgt.clone(), Box::new(sb));
|
||||
if let Some(a) = alias { let sb2 = crate::box_trait::StringBox::new(value); crate::runtime::modules_registry::set(a, Box::new(sb2)); }
|
||||
}
|
||||
}
|
||||
// Stage-1: Optional dependency tree bridge (log-only)
|
||||
if let Ok(dep_path) = std::env::var("NYASH_DEPS_JSON") {
|
||||
match std::fs::read_to_string(&dep_path) {
|
||||
Ok(s) => {
|
||||
let bytes = s.as_bytes().len();
|
||||
// Try to extract quick hints without failing
|
||||
let mut root_info = String::new();
|
||||
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&s) {
|
||||
if let Some(r) = v.get("root_path").and_then(|x| x.as_str()) { root_info = format!(" root='{}'", r); }
|
||||
}
|
||||
eprintln!("[deps] loaded {} bytes from{} {}", bytes, if root_info.is_empty(){""} else {":"}, root_info);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[deps] read error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase-15: JSON IR v0 bridge (stdin/file)
|
||||
if self.config.ny_parser_pipe || self.config.json_file.is_some() {
|
||||
let json = if let Some(path) = &self.config.json_file {
|
||||
@ -160,6 +252,41 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Env overrides for using rules
|
||||
if let Ok(paths) = std::env::var("NYASH_USING_PATH") {
|
||||
for p in paths.split(':') { let p = p.trim(); if !p.is_empty() { using_paths.push(p.to_string()); } }
|
||||
}
|
||||
if let Ok(mods) = std::env::var("NYASH_MODULES") {
|
||||
for ent in mods.split(',') {
|
||||
if let Some((k,v)) = ent.split_once('=') {
|
||||
let k = k.trim(); let v = v.trim();
|
||||
if !k.is_empty() && !v.is_empty() { pending_modules.push((k.to_string(), v.to_string())); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply pending modules to registry as StringBox (path or ns token)
|
||||
for (ns, path) in pending_modules.iter() {
|
||||
let sb = nyash_rust::box_trait::StringBox::new(path.clone());
|
||||
nyash_rust::runtime::modules_registry::set(ns.clone(), Box::new(sb));
|
||||
}
|
||||
// Resolve pending using with clear precedence and ambiguity handling
|
||||
let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1");
|
||||
let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1");
|
||||
let ctx = std::path::Path::new(filename).parent();
|
||||
for (ns, alias) in pending_using.iter() {
|
||||
let value = match resolve_using_target(ns, false, &pending_modules, &using_paths, ctx, strict, verbose) {
|
||||
Ok(v) => v,
|
||||
Err(e) => { eprintln!("❌ using: {}", e); std::process::exit(1); }
|
||||
};
|
||||
let sb = nyash_rust::box_trait::StringBox::new(value.clone());
|
||||
nyash_rust::runtime::modules_registry::set(ns.clone(), Box::new(sb));
|
||||
if let Some(a) = alias {
|
||||
let sb2 = nyash_rust::box_trait::StringBox::new(value);
|
||||
nyash_rust::runtime::modules_registry::set(a.clone(), Box::new(sb2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -248,6 +248,39 @@ impl NyashRunner {
|
||||
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
|
||||
};
|
||||
|
||||
// Stage-0: import loader (top-level only) — resolve path and register in modules registry
|
||||
if let nyash_rust::ast::ASTNode::Program { statements, .. } = &ast {
|
||||
for st in statements {
|
||||
if let nyash_rust::ast::ASTNode::ImportStatement { path, alias, .. } = st {
|
||||
// resolve path relative to current file if not absolute
|
||||
let mut p = std::path::PathBuf::from(path);
|
||||
if p.is_relative() {
|
||||
if let Some(dir) = std::path::Path::new(filename).parent() {
|
||||
p = dir.join(&p);
|
||||
}
|
||||
}
|
||||
let exists = p.exists();
|
||||
if !exists {
|
||||
if std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1") {
|
||||
eprintln!("❌ import: path not found: {} (from {})", p.display(), filename);
|
||||
process::exit(1);
|
||||
} else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") || std::env::var("NYASH_IMPORT_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[import] path not found (continuing): {}", p.display());
|
||||
}
|
||||
}
|
||||
let key = if let Some(a) = alias { a.clone() } else {
|
||||
std::path::Path::new(path)
|
||||
.file_stem().and_then(|s| s.to_str())
|
||||
.unwrap_or(path)
|
||||
.to_string()
|
||||
};
|
||||
let value = if exists { p.to_string_lossy().to_string() } else { path.clone() };
|
||||
let sb = nyash_rust::box_trait::StringBox::new(value);
|
||||
nyash_rust::runtime::modules_registry::set(key, Box::new(sb));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") && !quiet_pipe {
|
||||
println!("✅ Parse successful!");
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ pub fn init_bid_plugins() {
|
||||
|
||||
if let Ok(()) = init_global_plugin_host("nyash.toml") {
|
||||
if plugin_debug || cli_verbose {
|
||||
println!("🔌 plugin host initialized from nyash.toml");
|
||||
eprintln!("🔌 plugin host initialized from nyash.toml");
|
||||
// Show which plugin loader backend compiled in (enabled/stub)
|
||||
println!("[plugin-loader] backend={}", crate::runtime::plugin_loader_v2::backend_kind());
|
||||
}
|
||||
@ -29,7 +29,7 @@ pub fn init_bid_plugins() {
|
||||
}
|
||||
}
|
||||
if plugin_debug || cli_verbose {
|
||||
println!("✅ plugin host fully configured");
|
||||
eprintln!("✅ plugin host fully configured");
|
||||
}
|
||||
}
|
||||
} else if plugin_debug || cli_verbose {
|
||||
|
||||
@ -56,6 +56,7 @@ pub enum TokenType {
|
||||
FROM, // from (親メソッド呼び出し)
|
||||
WEAK, // weak (弱参照修飾子)
|
||||
USING, // using (名前空間インポート)
|
||||
IMPORT, // import (Phase 12.7)
|
||||
|
||||
// 演算子 (長いものから先に定義)
|
||||
SHIFT_LEFT, // << (bitwise shift-left)
|
||||
@ -145,6 +146,10 @@ pub struct NyashTokenizer {
|
||||
}
|
||||
|
||||
impl NyashTokenizer {
|
||||
#[inline]
|
||||
fn strict_12_7() -> bool {
|
||||
std::env::var("NYASH_STRICT_12_7").ok().as_deref() == Some("1")
|
||||
}
|
||||
/// 新しいトークナイザーを作成
|
||||
pub fn new(input: impl Into<String>) -> Self {
|
||||
let input_string = input.into();
|
||||
@ -248,7 +253,7 @@ impl NyashTokenizer {
|
||||
self.skip_whitespace(); // コメント後の空白もスキップ
|
||||
return self.tokenize_next();
|
||||
}
|
||||
Some('>') if self.peek_char() == Some('>') => {
|
||||
Some('>') if self.peek_char() == Some('>') && !Self::strict_12_7() => {
|
||||
self.advance();
|
||||
self.advance();
|
||||
Ok(Token::new(TokenType::SHIFT_RIGHT, start_line, start_column))
|
||||
@ -483,7 +488,7 @@ impl NyashTokenizer {
|
||||
}
|
||||
|
||||
// キーワードチェック
|
||||
let tok = match identifier.as_str() {
|
||||
let mut tok = match identifier.as_str() {
|
||||
"box" => TokenType::BOX,
|
||||
"global" => TokenType::GLOBAL,
|
||||
"singleton" => TokenType::SINGLETON,
|
||||
@ -497,8 +502,6 @@ impl NyashTokenizer {
|
||||
"return" => TokenType::RETURN,
|
||||
"function" => TokenType::FUNCTION,
|
||||
"fn" => TokenType::FN,
|
||||
// Alias support: `fn` as shorthand for function
|
||||
"fn" => TokenType::FUNCTION,
|
||||
"print" => TokenType::PRINT,
|
||||
"this" => TokenType::THIS,
|
||||
"me" => TokenType::ME,
|
||||
@ -509,6 +512,7 @@ impl NyashTokenizer {
|
||||
"await" => TokenType::AWAIT,
|
||||
"interface" => TokenType::INTERFACE,
|
||||
"include" => TokenType::INCLUDE,
|
||||
"import" => TokenType::IMPORT,
|
||||
"try" => TokenType::TRY,
|
||||
"catch" => TokenType::CATCH,
|
||||
"finally" => TokenType::FINALLY,
|
||||
@ -529,6 +533,23 @@ impl NyashTokenizer {
|
||||
_ => TokenType::IDENTIFIER(identifier.clone()),
|
||||
};
|
||||
|
||||
// 12.7 Strict mode: fallback extended keywords to IDENTIFIER
|
||||
if Self::strict_12_7() {
|
||||
let is_extended = matches!(tok,
|
||||
TokenType::INTERFACE
|
||||
| TokenType::USING
|
||||
| TokenType::INCLUDE
|
||||
| TokenType::OUTBOX
|
||||
| TokenType::NOWAIT
|
||||
| TokenType::OVERRIDE
|
||||
| TokenType::WEAK
|
||||
| TokenType::PACK
|
||||
);
|
||||
if is_extended {
|
||||
tok = TokenType::IDENTIFIER(identifier.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// 統一文法エンジンとの差分チェック(動作は変更しない)
|
||||
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
|
||||
// 安全に参照(初期導入のため、存在しない場合は無視)
|
||||
|
||||
Reference in New Issue
Block a user