tova 0.9.15 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tova",
3
- "version": "0.9.15",
3
+ "version": "0.10.2",
4
4
  "description": "Tova — a modern programming language that transpiles to JavaScript, unifying frontend and backend",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,6 +1,6 @@
1
1
  import { Scope, Symbol } from './scope.js';
2
2
  import { PIPE_TARGET } from '../parser/ast.js';
3
- import { BUILTIN_NAMES } from '../stdlib/inline.js';
3
+ import { BUILTIN_NAMES, DEPRECATED_NAMES, SNAKE_TO_CAMEL } from '../stdlib/inline.js';
4
4
  import { BlockRegistry } from '../registry/register-all.js';
5
5
  import {
6
6
  Type, PrimitiveType, NilType, AnyType, UnknownType,
@@ -854,6 +854,15 @@ export class Analyzer {
854
854
  this.visitExpression(node.collection);
855
855
  return;
856
856
  case 'CallExpression':
857
+ // W_DEPRECATED_STDLIB: warn on snake_case stdlib function usage
858
+ if (node.callee.type === 'Identifier' && DEPRECATED_NAMES.has(node.callee.name)) {
859
+ this.warn(
860
+ `'${node.callee.name}' is deprecated, use '${SNAKE_TO_CAMEL[node.callee.name]}' instead`,
861
+ node.callee.loc || node.loc,
862
+ `rename to ${SNAKE_TO_CAMEL[node.callee.name]}`,
863
+ { code: 'W_DEPRECATED_STDLIB' }
864
+ );
865
+ }
857
866
  // W_DANGEROUS_API: detect setTimeout/setInterval with string args
858
867
  if (node.callee.type === 'Identifier') {
859
868
  const calleeName = node.callee.name;
@@ -875,7 +875,9 @@ export class BaseCodegen {
875
875
  this.pushScope();
876
876
  for (const v of vars) this.declareVar(v);
877
877
  if (vars.length === 2) {
878
- p.push(`${this.i()}${labelPrefix}for${awaitKeyword} (const [${vars[0]}, ${vars[1]}] of ${tempVar}) {\n`);
878
+ // for i, v in collection => auto-enumerate: use .entries() for index-value pairs
879
+ const entriesExpr = `(Array.isArray(${tempVar}) ? ${tempVar}.entries() : Object.entries(${tempVar}))`;
880
+ p.push(`${this.i()}${labelPrefix}for${awaitKeyword} (const [${vars[0]}, ${vars[1]}] of ${entriesExpr}) {\n`);
879
881
  } else {
880
882
  p.push(`${this.i()}${labelPrefix}for${awaitKeyword} (const ${vars[0]} of ${tempVar}) {\n`);
881
883
  }
@@ -904,7 +906,9 @@ export class BaseCodegen {
904
906
  for (const v of vars) this.declareVar(v);
905
907
  const p = [];
906
908
  if (vars.length === 2) {
907
- p.push(`${this.i()}${labelPrefix}for${awaitKeyword} (const [${vars[0]}, ${vars[1]}] of ${iterExpr}) {\n`);
909
+ // for i, v in collection => auto-enumerate: use .entries() for index-value pairs
910
+ const entriesExpr = `(Array.isArray(${iterExpr}) ? ${iterExpr}.entries() : Object.entries(${iterExpr}))`;
911
+ p.push(`${this.i()}${labelPrefix}for${awaitKeyword} (const [${vars[0]}, ${vars[1]}] of ${entriesExpr}) {\n`);
908
912
  } else {
909
913
  p.push(`${this.i()}${labelPrefix}for${awaitKeyword} (const ${vars[0]} of ${iterExpr}) {\n`);
910
914
  }
package/src/lsp/server.js CHANGED
@@ -8,7 +8,7 @@ import { Analyzer } from '../analyzer/analyzer.js';
8
8
  import { TokenType } from '../lexer/tokens.js';
9
9
  import { Formatter } from '../formatter/formatter.js';
10
10
  import { TypeRegistry } from '../analyzer/type-registry.js';
11
- import { BUILTIN_NAMES, BUILTIN_FUNCTIONS } from '../stdlib/inline.js';
11
+ import { BUILTIN_NAMES, BUILTIN_FUNCTIONS, DEPRECATED_NAMES, SNAKE_TO_CAMEL } from '../stdlib/inline.js';
12
12
 
13
13
  class TovaLanguageServer {
14
14
  static MAX_CACHE_SIZE = 100; // max cached diagnostics entries
@@ -532,7 +532,12 @@ class TovaLanguageServer {
532
532
  for (const fn of BUILTIN_NAMES) {
533
533
  if (fn.startsWith(prefix) && !fn.startsWith('__')) {
534
534
  const detail = this._getBuiltinDetail(fn);
535
- items.push({ label: fn, kind: 3 /* Function */, detail });
535
+ const item = { label: fn, kind: 3 /* Function */, detail };
536
+ if (DEPRECATED_NAMES.has(fn)) {
537
+ item.tags = [1]; // CompletionItemTag.Deprecated
538
+ item.detail = `(deprecated) use ${SNAKE_TO_CAMEL[fn]} → ${detail}`;
539
+ }
540
+ items.push(item);
536
541
  }
537
542
  }
538
543
  // Runtime types
@@ -1618,6 +1618,238 @@ function __chart_empty(w, h, msg) { return '<svg xmlns="http://www.w3.org/2000/s
1618
1618
  heatmap: `function heatmap(data, opts) { if (!opts) opts = {}; var rows = __chart_getRows(data); var width = opts.width || 600; var height = opts.height || 400; if (rows.length === 0) return __chart_empty(width, height, 'No data'); var xFn = opts.x; var yFn = opts.y; var valueFn = opts.value; var title = opts.title || ''; var margin = { top: title ? 50 : 40, right: 40, bottom: 60, left: 80 }; var xCats = []; var yCats = []; var xSet = new Set(); var ySet = new Set(); for (var i = 0; i < rows.length; i++) { var xv = String(xFn(rows[i])); var yv = String(yFn(rows[i])); if (!xSet.has(xv)) { xSet.add(xv); xCats.push(xv); } if (!ySet.has(yv)) { ySet.add(yv); yCats.push(yv); } } var grid = {}; var vMn = Infinity; var vMx = -Infinity; for (var i = 0; i < rows.length; i++) { var xv = String(xFn(rows[i])); var yv = String(yFn(rows[i])); var val = Number(valueFn(rows[i])); grid[xv + '|' + yv] = val; if (val < vMn) vMn = val; if (val > vMx) vMx = val; } var vR = vMx - vMn || 1; var plotW = width - margin.left - margin.right; var plotH = height - margin.top - margin.bottom; var cellW = plotW / xCats.length; var cellH = plotH / yCats.length; function hc(val) { var t = (val - vMn) / vR; var r = Math.round(255 - t * (255 - 79)); var g = Math.round(255 - t * (255 - 70)); var b = Math.round(255 - t * (255 - 229)); return 'rgb(' + r + ',' + g + ',' + b + ')'; } var p = []; p.push('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + width + ' ' + height + '" width="' + width + '" height="' + height + '" style="font-family:system-ui,sans-serif">'); if (title) p.push('<text x="' + (width / 2) + '" y="24" text-anchor="middle" font-size="16" font-weight="bold" fill="#111">' + __chart_esc(title) + '</text>'); for (var xi = 0; xi < xCats.length; xi++) { for (var yi = 0; yi < yCats.length; yi++) { var key = xCats[xi] + '|' + yCats[yi]; var val = grid[key]; var rx = margin.left + xi * cellW; var ry = margin.top + yi * cellH; var fill = val !== undefined ? hc(val) : '#f3f4f6'; p.push('<rect x="' + rx + '" y="' + ry + '" width="' + cellW + '" height="' + cellH + '" fill="' + fill + '" stroke="#fff" stroke-width="1"/>'); if (val !== undefined) { var tc = ((val - vMn) / vR) > 0.5 ? '#fff' : '#111'; p.push('<text x="' + (rx + cellW / 2) + '" y="' + (ry + cellH / 2 + 4) + '" text-anchor="middle" font-size="11" fill="' + tc + '">' + __chart_formatNum(val) + '</text>'); } } } for (var xi = 0; xi < xCats.length; xi++) { var lx = margin.left + xi * cellW + cellW / 2; var ly = margin.top + plotH + 16; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="middle" font-size="11" fill="#666">' + __chart_esc(xCats[xi]) + '</text>'); } for (var yi = 0; yi < yCats.length; yi++) { var lx = margin.left - 8; var ly = margin.top + yi * cellH + cellH / 2 + 4; p.push('<text x="' + lx + '" y="' + ly + '" text-anchor="end" font-size="11" fill="#666">' + __chart_esc(yCats[yi]) + '</text>'); } p.push('</svg>'); return p.join('\\n'); }`,
1619
1619
  };
1620
1620
 
1621
+ // ─── Snake_case → camelCase Migration ──────────────────────────
1622
+ // Maps deprecated snake_case stdlib names to their camelCase replacements.
1623
+ // Both names work; old names emit a deprecation warning via the analyzer.
1624
+ export const SNAKE_TO_CAMEL = {
1625
+ // Collections / iterators
1626
+ type_of: 'typeOf',
1627
+ flat_map: 'flatMap',
1628
+ group_by: 'groupBy',
1629
+ sort_by: 'sortBy',
1630
+ find_index: 'findIndex',
1631
+ min_by: 'minBy',
1632
+ max_by: 'maxBy',
1633
+ sum_by: 'sumBy',
1634
+ zip_with: 'zipWith',
1635
+ sliding_window: 'slidingWindow',
1636
+ binary_search: 'binarySearch',
1637
+ is_sorted: 'isSorted',
1638
+ insert_at: 'insertAt',
1639
+ remove_at: 'removeAt',
1640
+ update_at: 'updateAt',
1641
+ repeat_value: 'repeatValue',
1642
+ from_entries: 'fromEntries',
1643
+ has_key: 'hasKey',
1644
+ map_values: 'mapValues',
1645
+ drop_duplicates: 'dropDuplicates',
1646
+ drop_nil: 'dropNil',
1647
+ fill_nil: 'fillNil',
1648
+ filter_ok: 'filterOk',
1649
+ filter_err: 'filterErr',
1650
+ // Strings
1651
+ starts_with: 'startsWith',
1652
+ ends_with: 'endsWith',
1653
+ title_case: 'titleCase',
1654
+ snake_case: 'snakeCase',
1655
+ camel_case: 'camelCase',
1656
+ kebab_case: 'kebabCase',
1657
+ replace_first: 'replaceFirst',
1658
+ pad_start: 'padStart',
1659
+ pad_end: 'padEnd',
1660
+ char_at: 'charAt',
1661
+ trim_start: 'trimStart',
1662
+ trim_end: 'trimEnd',
1663
+ index_of: 'indexOf',
1664
+ last_index_of: 'lastIndexOf',
1665
+ count_of: 'countOf',
1666
+ reverse_str: 'reverseStr',
1667
+ indent_str: 'indentStr',
1668
+ escape_html: 'escapeHtml',
1669
+ unescape_html: 'unescapeHtml',
1670
+ word_wrap: 'wordWrap',
1671
+ is_empty: 'isEmpty',
1672
+ // Conversions
1673
+ to_int: 'toInt',
1674
+ to_float: 'toFloat',
1675
+ to_string: 'toString',
1676
+ to_bool: 'toBool',
1677
+ to_hex: 'toHex',
1678
+ to_binary: 'toBinary',
1679
+ to_octal: 'toOctal',
1680
+ to_fixed: 'toFixed',
1681
+ to_radians: 'toRadians',
1682
+ to_degrees: 'toDegrees',
1683
+ // Math
1684
+ is_nan: 'isNaN',
1685
+ is_finite: 'isFinite',
1686
+ is_close: 'isClose',
1687
+ random_int: 'randomInt',
1688
+ random_float: 'randomFloat',
1689
+ format_number: 'formatNumber',
1690
+ // Testing
1691
+ assert_eq: 'assertEq',
1692
+ assert_ne: 'assertNe',
1693
+ assert_throws: 'assertThrows',
1694
+ assert_snapshot: 'assertSnapshot',
1695
+ create_spy: 'createSpy',
1696
+ create_mock: 'createMock',
1697
+ // JSON / encoding
1698
+ json_parse: 'jsonParse',
1699
+ json_stringify: 'jsonStringify',
1700
+ json_pretty: 'jsonPretty',
1701
+ base64_encode: 'base64Encode',
1702
+ base64_decode: 'base64Decode',
1703
+ url_encode: 'urlEncode',
1704
+ url_decode: 'urlDecode',
1705
+ hex_encode: 'hexEncode',
1706
+ hex_decode: 'hexDecode',
1707
+ // URL
1708
+ parse_url: 'parseUrl',
1709
+ build_url: 'buildUrl',
1710
+ parse_query: 'parseQuery',
1711
+ build_query: 'buildQuery',
1712
+ // Date / time
1713
+ date_parse: 'dateParse',
1714
+ date_format: 'dateFormat',
1715
+ date_from: 'dateFrom',
1716
+ date_add: 'dateAdd',
1717
+ date_diff: 'dateDiff',
1718
+ date_part: 'datePart',
1719
+ now_iso: 'nowIso',
1720
+ time_ago: 'timeAgo',
1721
+ // Regex
1722
+ regex_test: 'regexTest',
1723
+ regex_match: 'regexMatch',
1724
+ regex_find_all: 'regexFindAll',
1725
+ regex_replace: 'regexReplace',
1726
+ regex_split: 'regexSplit',
1727
+ regex_capture: 'regexCapture',
1728
+ regex_builder: 'regexBuilder',
1729
+ // Validation
1730
+ is_email: 'isEmail',
1731
+ is_url: 'isUrl',
1732
+ is_numeric: 'isNumeric',
1733
+ is_alpha: 'isAlpha',
1734
+ is_alphanumeric: 'isAlphanumeric',
1735
+ is_uuid: 'isUuid',
1736
+ is_hex: 'isHex',
1737
+ // Sets
1738
+ is_subset: 'isSubset',
1739
+ is_superset: 'isSuperset',
1740
+ symmetric_difference: 'symmetricDifference',
1741
+ // Functional
1742
+ compare_by: 'compareBy',
1743
+ pipe_fn: 'pipeFn',
1744
+ try_fn: 'tryFn',
1745
+ try_async: 'tryAsync',
1746
+ schema_of: 'schemaOf',
1747
+ // File system
1748
+ is_file: 'isFile',
1749
+ is_dir: 'isDir',
1750
+ is_symlink: 'isSymlink',
1751
+ glob_files: 'globFiles',
1752
+ read_text: 'readText',
1753
+ read_bytes: 'readBytes',
1754
+ write_text: 'writeText',
1755
+ read_stdin: 'readStdin',
1756
+ read_lines: 'readLines',
1757
+ file_stat: 'fileStat',
1758
+ file_size: 'fileSize',
1759
+ // Path
1760
+ path_join: 'pathJoin',
1761
+ path_dirname: 'pathDirname',
1762
+ path_basename: 'pathBasename',
1763
+ path_resolve: 'pathResolve',
1764
+ path_ext: 'pathExt',
1765
+ path_relative: 'pathRelative',
1766
+ // Script
1767
+ script_path: 'scriptPath',
1768
+ script_dir: 'scriptDir',
1769
+ // Process
1770
+ set_env: 'setEnv',
1771
+ on_signal: 'onSignal',
1772
+ parse_args: 'parseArgs',
1773
+ // CLI
1774
+ choose_many: 'chooseMany',
1775
+ // Concurrency
1776
+ parallel_map: 'parallelMap',
1777
+ // Charts
1778
+ bar_chart: 'barChart',
1779
+ line_chart: 'lineChart',
1780
+ scatter_chart: 'scatterChart',
1781
+ pie_chart: 'pieChart',
1782
+ // Table operations
1783
+ table_where: 'tableWhere',
1784
+ table_select: 'tableSelect',
1785
+ table_derive: 'tableDerive',
1786
+ table_group_by: 'tableGroupBy',
1787
+ table_agg: 'tableAgg',
1788
+ table_sort_by: 'tableSortBy',
1789
+ table_limit: 'tableLimit',
1790
+ table_join: 'tableJoin',
1791
+ table_pivot: 'tablePivot',
1792
+ table_unpivot: 'tableUnpivot',
1793
+ table_explode: 'tableExplode',
1794
+ table_union: 'tableUnion',
1795
+ table_drop_duplicates: 'tableDropDuplicates',
1796
+ table_rename: 'tableRename',
1797
+ table_window: 'tableWindow',
1798
+ table_sample: 'tableSample',
1799
+ table_stratified_sample: 'tableStratifiedSample',
1800
+ // Aggregation functions
1801
+ agg_sum: 'aggSum',
1802
+ agg_count: 'aggCount',
1803
+ agg_mean: 'aggMean',
1804
+ agg_median: 'aggMedian',
1805
+ agg_min: 'aggMin',
1806
+ agg_max: 'aggMax',
1807
+ // Window functions
1808
+ win_row_number: 'winRowNumber',
1809
+ win_rank: 'winRank',
1810
+ win_dense_rank: 'winDenseRank',
1811
+ win_percent_rank: 'winPercentRank',
1812
+ win_ntile: 'winNtile',
1813
+ win_lag: 'winLag',
1814
+ win_lead: 'winLead',
1815
+ win_first_value: 'winFirstValue',
1816
+ win_last_value: 'winLastValue',
1817
+ win_running_sum: 'winRunningSum',
1818
+ win_running_count: 'winRunningCount',
1819
+ win_running_avg: 'winRunningAvg',
1820
+ win_running_min: 'winRunningMin',
1821
+ win_running_max: 'winRunningMax',
1822
+ win_moving_avg: 'winMovingAvg',
1823
+ // Typed arrays
1824
+ typed_sum: 'typedSum',
1825
+ typed_dot: 'typedDot',
1826
+ typed_add: 'typedAdd',
1827
+ typed_scale: 'typedScale',
1828
+ typed_map: 'typedMap',
1829
+ typed_reduce: 'typedReduce',
1830
+ typed_sort: 'typedSort',
1831
+ typed_zeros: 'typedZeros',
1832
+ typed_ones: 'typedOnes',
1833
+ typed_fill: 'typedFill',
1834
+ typed_linspace: 'typedLinspace',
1835
+ typed_norm: 'typedNorm',
1836
+ typed_range: 'typedRange',
1837
+ };
1838
+
1839
+ export const DEPRECATED_NAMES = new Set(Object.keys(SNAKE_TO_CAMEL));
1840
+
1841
+ // Reverse mapping: camelCase → snake_case (for tooling)
1842
+ export const CAMEL_TO_SNAKE = Object.fromEntries(
1843
+ Object.entries(SNAKE_TO_CAMEL).map(([s, c]) => [c, s])
1844
+ );
1845
+
1846
+ // Generate camelCase wrapper functions that delegate to snake_case originals
1847
+ for (const [snake, camel] of Object.entries(SNAKE_TO_CAMEL)) {
1848
+ if (BUILTIN_FUNCTIONS[snake] && !BUILTIN_FUNCTIONS[camel]) {
1849
+ BUILTIN_FUNCTIONS[camel] = `function ${camel}() { return ${snake}.apply(null, arguments); }`;
1850
+ }
1851
+ }
1852
+
1621
1853
  // All known builtin names for matching
1622
1854
  export const BUILTIN_NAMES = new Set(Object.keys(BUILTIN_FUNCTIONS));
1623
1855
 
@@ -1709,6 +1941,14 @@ export const STDLIB_DEPS = {
1709
1941
  heatmap: ['Table', '__chart_helpers'],
1710
1942
  };
1711
1943
 
1944
+ // Generate STDLIB_DEPS entries for camelCase wrappers
1945
+ // Each camelCase wrapper depends on its snake_case original (+ that original's deps)
1946
+ for (const [snake, camel] of Object.entries(SNAKE_TO_CAMEL)) {
1947
+ if (BUILTIN_FUNCTIONS[camel]) {
1948
+ STDLIB_DEPS[camel] = [snake, ...(STDLIB_DEPS[snake] || [])];
1949
+ }
1950
+ }
1951
+
1712
1952
  // Resolve all transitive dependencies for a set of used names
1713
1953
  export function resolveStdlibDeps(usedNames) {
1714
1954
  const resolved = new Set(usedNames);
@@ -1757,16 +1997,20 @@ function _topoSort(names) {
1757
1997
  // Only includes non-internal, non-table functions for backward compat with tests/playground
1758
1998
  const _LEGACY_NAMES = [
1759
1999
  'print', 'len', 'range', 'enumerate', 'sum', 'sorted', 'reversed', 'zip',
1760
- 'min', 'max', 'type_of', 'filter', 'map', 'find', 'any', 'all', 'flat_map',
1761
- 'reduce', 'unique', 'group_by', 'chunk', 'flatten', 'take', 'drop', 'first',
1762
- 'last', 'count', 'partition', 'abs', 'floor', 'ceil', 'round', 'clamp',
1763
- 'sqrt', 'pow', 'random', 'trim', 'split', 'join', 'replace', 'repeat',
2000
+ 'min', 'max', 'type_of', 'typeOf', 'filter', 'map', 'find', 'any', 'all',
2001
+ 'flat_map', 'flatMap', 'reduce', 'unique', 'group_by', 'groupBy',
2002
+ 'chunk', 'flatten', 'take', 'drop', 'first', 'last', 'count', 'partition',
2003
+ 'abs', 'floor', 'ceil', 'round', 'clamp', 'sqrt', 'pow', 'random',
2004
+ 'trim', 'split', 'join', 'replace', 'repeat',
1764
2005
  'keys', 'values', 'entries', 'merge', 'freeze', 'clone', 'sleep',
1765
- 'upper', 'lower', 'contains', 'starts_with', 'ends_with', 'chars', 'words',
1766
- 'lines', 'capitalize', 'title_case', 'snake_case', 'camel_case',
1767
- 'assert_eq', 'assert_ne', 'assert', 'assert_throws',
1768
- 'create_spy', 'create_mock',
1769
- 'parallel_map',
2006
+ 'upper', 'lower', 'contains', 'starts_with', 'startsWith',
2007
+ 'ends_with', 'endsWith', 'chars', 'words', 'lines', 'capitalize',
2008
+ 'title_case', 'titleCase', 'snake_case', 'snakeCase',
2009
+ 'camel_case', 'camelCase',
2010
+ 'assert_eq', 'assertEq', 'assert_ne', 'assertNe', 'assert',
2011
+ 'assert_throws', 'assertThrows',
2012
+ 'create_spy', 'createSpy', 'create_mock', 'createMock',
2013
+ 'parallel_map', 'parallelMap',
1770
2014
  ];
1771
2015
  export const BUILTINS = _LEGACY_NAMES.map(n => BUILTIN_FUNCTIONS[n]).join('\n');
1772
2016
 
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/embed-runtime.js — do not edit
2
- export const VERSION = "0.9.15";
2
+ export const VERSION = "0.10.2";