@@ -715,25 +715,24 @@ impl Evaluator {
716 // --- @data (load JSON file into variables) ---
716 // --- @data (load JSON file into variables) ---
718− // @data file.json top-level keys become variables
719 // @data $name file.json values as $name.key
718 // @data $name file.json values as $name.key
720 // @data $name dir/*.json each file as $name.STEM.key; $name
719 // @data $name dir/*.json each file as $name.STEM.key; $name
721 // lists the stems, $name._count counts them
720 // lists the stems, $name._count counts them
722 // @data $name env:NAME [DEFAULT] an environment variable
721 // @data $name env:NAME [DEFAULT] an environment variable
723 if let Some(rest) = content.strip_prefix("@data ") {
722 if let Some(rest) = content.strip_prefix("@data ") {
724 let rest = rest.trim();
723 let rest = rest.trim();
725− let (prefix, filename) = match rest.strip_prefix('$') {
724+ let Some((prefix, filename)) = rest
726− Some(named) => match named.split_once(char::is_whitespace) {
726+ .and_then(|named| named.split_once(char::is_whitespace))
727− Some((name, source)) => (name.to_string(), source.trim().to_string()),
727+ .map(|(name, source)| (name.to_string(), source.trim().to_string()))
728− None => {
728+ else {
729− return Err(ParseError {
729+ return Err(ParseError {
730− line: line_num,
730+ line: line_num,
731− message: "@data requires: @data $name SOURCE or @data file.json"
731+ message: format!(
732− .to_string(),
732+ "@data needs a name: write `@data $name {}` and use `$name.key`",
733+ rest.trim_start_matches('$')
736− None => (String::new(), rest.to_string()),
739 if let Some(env) = filename.strip_prefix("env:") {
738 if let Some(env) = filename.strip_prefix("env:") {
@@ -741,12 +740,6 @@ impl Evaluator {
741 Some((var, default)) => (var, Some(default.trim())),
740 Some((var, default)) => (var, Some(default.trim())),
742 None => (env, None),
741 None => (env, None),
744− if prefix.is_empty() {
745− return Err(ParseError {
747− message: format!("@data env:{} needs a name: @data $name env:{}", var, var),
750 let value = std::env::var(var)
743 let value = std::env::var(var)
752 .or_else(|| default.map(|d| substitute_vars(d, &ctx.variables)));
745 .or_else(|| default.map(|d| substitute_vars(d, &ctx.variables)));
@@ -768,12 +761,6 @@ impl Evaluator {
769 let filename = substitute_vars(&filename, &ctx.variables);
762 let filename = substitute_vars(&filename, &ctx.variables);
770 if filename.contains('*') {
763 if filename.contains('*') {
771− if prefix.is_empty() {
772− return Err(ParseError {
774− message: format!("@data {} needs a name: @data $name {}", filename, filename),
777 self.load_data_glob(&prefix, &filename, line_num, &content, ctx);
764 self.load_data_glob(&prefix, &filename, line_num, &content, ctx);
778 return Ok(None);
765 return Ok(None);
@@ -800,32 +787,10 @@ impl Evaluator {
801 match parse_json_with_error(&json_text) {
788 match parse_json_with_error(&json_text) {
802 Ok(json) => {
789 Ok(json) => {
803− if prefix.is_empty() {
804− // No prefix: top-level object keys become variables directly
805− if let JsonValue::Object(pairs) = &json {
806− for (key, val) in pairs {
807− let mut sub = HashMap::new();
790+ let mut sub = HashMap::new();
808− flatten_json(key, val, &mut sub);
810− ctx.variables.insert(k, v);
814− ctx.diagnostics.push(Diagnostic {
817− message: "@data without prefix requires a JSON object at top level"
819− severity: Severity::Error,
820− source_line: Some(content.clone()),
824− let mut sub = HashMap::new();
825− flatten_json(&prefix, &json, &mut sub);
791+ flatten_json(&prefix, &json, &mut sub);
826− for (k, v) in sub {
792+ for (k, v) in sub {
827− ctx.variables.insert(k, v);
793+ ctx.variables.insert(k, v);
831 Err(detail) => {
796 Err(detail) => {