@aperturesyndicate/synx-format 3.6.0
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/LICENSE +21 -0
- package/README.md +144 -0
- package/SPECIFICATION.md +6 -0
- package/bin/synx.js +146 -0
- package/dist/browser.d.ts +17 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +72 -0
- package/dist/browser.js.map +1 -0
- package/dist/calc.d.ts +16 -0
- package/dist/calc.d.ts.map +1 -0
- package/dist/calc.js +140 -0
- package/dist/calc.js.map +1 -0
- package/dist/demo-browser.html +153 -0
- package/dist/engine.d.ts +9 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +970 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +193 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +810 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +12 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +442 -0
- package/dist/parser.js.map +1 -0
- package/dist/synx.browser.js +29 -0
- package/dist/synx.browser.js.map +7 -0
- package/dist/synx.browser.mjs +28 -0
- package/dist/synx.browser.mjs.map +7 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 APERTURESyndicate
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# SYNX for JS/TS — @aperturesyndicate/synx
|
|
2
|
+
|
|
3
|
+
The official JavaScript & TypeScript parser for the SYNX format.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @aperturesyndicate/synx
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
const Synx = require('@aperturesyndicate/synx');
|
|
15
|
+
|
|
16
|
+
// Load from file
|
|
17
|
+
const data = Synx.loadSync('config.synx');
|
|
18
|
+
console.log(data.server.port); // 8080
|
|
19
|
+
|
|
20
|
+
// Parse from string
|
|
21
|
+
const data2 = Synx.parse('name Wario\nage 30');
|
|
22
|
+
console.log(data2.name); // "Wario"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### TypeScript
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import Synx from '@aperturesyndicate/synx';
|
|
29
|
+
|
|
30
|
+
interface Config {
|
|
31
|
+
server: { port: number; host: string };
|
|
32
|
+
app_name: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const data = Synx.loadSync<Config>('config.synx');
|
|
36
|
+
console.log(data.server.port); // typed as number
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API
|
|
40
|
+
|
|
41
|
+
| Method | Description |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `Synx.parse<T>(text, options?)` | Parse a .synx string → object |
|
|
44
|
+
| `Synx.loadSync<T>(path, options?)` | Load & parse file (sync) |
|
|
45
|
+
| `Synx.load<T>(path, options?)` | Load & parse file (async, returns Promise) |
|
|
46
|
+
| `Synx.stringify(obj, active?)` | Serialize object → .synx string |
|
|
47
|
+
|
|
48
|
+
### Options
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
{
|
|
52
|
+
basePath?: string; // For :include resolution
|
|
53
|
+
env?: Record<string, string>; // Override env vars
|
|
54
|
+
region?: string; // For :geo ("RU", "US", etc.)
|
|
55
|
+
strict?: boolean; // Throw on INCLUDE_ERR/WATCH_ERR/CALC_ERR/CONSTRAINT_ERR
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`strict: true` enables fail-fast behavior for production: marker runtime errors throw instead of silently remaining in the output object as error strings.
|
|
60
|
+
|
|
61
|
+
## CLI
|
|
62
|
+
|
|
63
|
+
Install globally:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install -g @aperturesyndicate/synx
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Commands
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Convert to JSON/YAML/TOML/.env
|
|
73
|
+
synx convert config.synx --format json
|
|
74
|
+
synx convert config.synx --format yaml > values.yaml
|
|
75
|
+
synx convert config.synx --format toml
|
|
76
|
+
synx convert config.synx --format env > .env
|
|
77
|
+
|
|
78
|
+
# Validate (strict mode, for CI/CD)
|
|
79
|
+
synx validate config.synx --strict
|
|
80
|
+
|
|
81
|
+
# Watch for changes
|
|
82
|
+
synx watch config.synx --format json
|
|
83
|
+
synx watch config.synx --exec "nginx -s reload"
|
|
84
|
+
|
|
85
|
+
# Extract JSON Schema from constraints
|
|
86
|
+
synx schema config.synx
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Export Formats
|
|
90
|
+
|
|
91
|
+
Convert parsed SYNX to other formats programmatically:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const config = Synx.loadSync('config.synx');
|
|
95
|
+
|
|
96
|
+
Synx.toJSON(config); // JSON (pretty)
|
|
97
|
+
Synx.toJSON(config, false); // JSON (compact)
|
|
98
|
+
Synx.toYAML(config); // YAML
|
|
99
|
+
Synx.toTOML(config); // TOML
|
|
100
|
+
Synx.toEnv(config); // KEY=VALUE
|
|
101
|
+
Synx.toEnv(config, 'PREFIX'); // PREFIX_KEY=VALUE
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## File Watcher
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const handle = Synx.watch('config.synx', (config, error) => {
|
|
108
|
+
if (error) return console.error(error);
|
|
109
|
+
console.log('Config reloaded:', config);
|
|
110
|
+
}, { strict: true });
|
|
111
|
+
|
|
112
|
+
handle.close(); // stop watching
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Schema Export
|
|
116
|
+
|
|
117
|
+
Extract constraints as JSON Schema:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const schema = Synx.schema(`
|
|
121
|
+
!active
|
|
122
|
+
app_name[required, min:3, max:30] TotalWario
|
|
123
|
+
volume[min:0, max:100, type:int] 75
|
|
124
|
+
`);
|
|
125
|
+
// { "$schema": "...", "properties": { "app_name": { ... } }, "required": ["app_name"] }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Other Languages
|
|
129
|
+
|
|
130
|
+
- **Python** — [synx-format](https://pypi.org/project/synx-format/) on PyPI
|
|
131
|
+
- **Rust** — [synx](https://crates.io/crates/synx) on crates.io
|
|
132
|
+
```bash
|
|
133
|
+
cargo add synx
|
|
134
|
+
```
|
|
135
|
+
```rust
|
|
136
|
+
use synx::Synx;
|
|
137
|
+
|
|
138
|
+
let data = Synx::load("config.synx")?;
|
|
139
|
+
println!("{}", &data["server"]["port"]);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT — © APERTURESyndicate
|
package/SPECIFICATION.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# SYNX specification
|
|
2
|
+
|
|
3
|
+
The normative specification files live in the monorepo (not duplicated here for npm):
|
|
4
|
+
|
|
5
|
+
- English: [`docs/spec/SPECIFICATION_EN.md`](https://github.com/kaiserrberg/synx-format/blob/main/docs/spec/SPECIFICATION_EN.md)
|
|
6
|
+
- Russian: [`docs/spec/SPECIFICATION_RU.md`](https://github.com/kaiserrberg/synx-format/blob/main/docs/spec/SPECIFICATION_RU.md)
|
package/bin/synx.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const Synx = require('../dist/index');
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const command = args[0];
|
|
10
|
+
|
|
11
|
+
function usage() {
|
|
12
|
+
console.log(`
|
|
13
|
+
SYNX CLI — The Active Data Format (v3.6.0)
|
|
14
|
+
|
|
15
|
+
DEPRECATED: This Node.js CLI is superseded by the native Rust CLI.
|
|
16
|
+
Install: cargo install synx-cli
|
|
17
|
+
Docs: https://github.com/kaiserrberg/synx-format#cli-rust
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
synx convert <file.synx> [--format json|yaml|toml|env] [--strict] [--active]
|
|
21
|
+
synx validate <file.synx> [--strict]
|
|
22
|
+
synx watch <file.synx> [--format json] [--exec <command>]
|
|
23
|
+
synx schema <file.synx>
|
|
24
|
+
|
|
25
|
+
Commands:
|
|
26
|
+
convert Parse .synx and output in another format (default: json)
|
|
27
|
+
validate Parse with strict mode — exits 1 on any marker error
|
|
28
|
+
watch Watch file for changes, re-parse and print or exec command
|
|
29
|
+
schema Extract constraints as JSON Schema
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
--format, -f Output format: json (default), yaml, toml, env
|
|
33
|
+
--strict Throw on INCLUDE_ERR / WATCH_ERR / CALC_ERR / CONSTRAINT_ERR
|
|
34
|
+
--active Force active mode resolution
|
|
35
|
+
--exec, -e Command to run after each re-parse (watch mode)
|
|
36
|
+
--help, -h Show this help
|
|
37
|
+
`);
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getFlag(flag, alias) {
|
|
42
|
+
const idx = args.indexOf(flag);
|
|
43
|
+
const aidx = alias ? args.indexOf(alias) : -1;
|
|
44
|
+
return idx !== -1 || aidx !== -1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getFlagValue(flag, alias, defaultVal) {
|
|
48
|
+
let idx = args.indexOf(flag);
|
|
49
|
+
if (idx === -1 && alias) idx = args.indexOf(alias);
|
|
50
|
+
if (idx === -1 || idx + 1 >= args.length) return defaultVal;
|
|
51
|
+
return args[idx + 1];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!command || command === '--help' || command === '-h') usage();
|
|
55
|
+
|
|
56
|
+
const file = args[1];
|
|
57
|
+
if (!file) {
|
|
58
|
+
console.error('Error: no file specified');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const absPath = path.resolve(file);
|
|
63
|
+
if (!fs.existsSync(absPath)) {
|
|
64
|
+
console.error(`Error: file not found: ${absPath}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const format = getFlagValue('--format', '-f', 'json');
|
|
69
|
+
const strict = getFlag('--strict');
|
|
70
|
+
const execCmd = getFlagValue('--exec', '-e', null);
|
|
71
|
+
|
|
72
|
+
function parseFile(filePath) {
|
|
73
|
+
const text = fs.readFileSync(filePath, 'utf-8');
|
|
74
|
+
return Synx.parse(text, {
|
|
75
|
+
basePath: path.dirname(filePath),
|
|
76
|
+
strict,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatOutput(obj) {
|
|
81
|
+
switch (format) {
|
|
82
|
+
case 'json': return Synx.toJSON(obj);
|
|
83
|
+
case 'yaml': return Synx.toYAML(obj);
|
|
84
|
+
case 'toml': return Synx.toTOML(obj);
|
|
85
|
+
case 'env': return Synx.toEnv(obj);
|
|
86
|
+
default:
|
|
87
|
+
console.error(`Unknown format: ${format}. Use json, yaml, toml, or env.`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
switch (command) {
|
|
93
|
+
case 'convert': {
|
|
94
|
+
try {
|
|
95
|
+
const obj = parseFile(absPath);
|
|
96
|
+
process.stdout.write(formatOutput(obj));
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.error(e.message);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
case 'validate': {
|
|
105
|
+
try {
|
|
106
|
+
const text = fs.readFileSync(absPath, 'utf-8');
|
|
107
|
+
Synx.parse(text, { basePath: path.dirname(absPath), strict: true });
|
|
108
|
+
console.log('OK');
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.error('FAIL:', e.message);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case 'watch': {
|
|
117
|
+
console.error(`[synx] watching ${absPath}`);
|
|
118
|
+
const handle = Synx.watch(absPath, (config, error) => {
|
|
119
|
+
if (error) {
|
|
120
|
+
console.error(`[synx] error: ${error.message}`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (execCmd) {
|
|
124
|
+
const { execSync } = require('child_process');
|
|
125
|
+
try {
|
|
126
|
+
execSync(execCmd, { stdio: 'inherit' });
|
|
127
|
+
} catch { /* command failed */ }
|
|
128
|
+
} else {
|
|
129
|
+
process.stdout.write(formatOutput(config));
|
|
130
|
+
}
|
|
131
|
+
}, { basePath: path.dirname(absPath), strict });
|
|
132
|
+
process.on('SIGINT', () => { handle.close(); process.exit(0); });
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
case 'schema': {
|
|
137
|
+
const text = fs.readFileSync(absPath, 'utf-8');
|
|
138
|
+
const schema = Synx.schema(text);
|
|
139
|
+
console.log(JSON.stringify(schema, null, 2));
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
default:
|
|
144
|
+
console.error(`Unknown command: ${command}`);
|
|
145
|
+
usage();
|
|
146
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SYNX Browser Bundle — @aperturesyndicate/synx
|
|
3
|
+
*
|
|
4
|
+
* Lightweight browser-compatible build.
|
|
5
|
+
* No Node.js dependencies (fs, path).
|
|
6
|
+
* Provides: parse, stringify.
|
|
7
|
+
*/
|
|
8
|
+
import type { SynxObject, SynxOptions } from './types';
|
|
9
|
+
export type { SynxObject, SynxOptions, SynxValue, SynxArray, SynxPrimitive } from './types';
|
|
10
|
+
export { SynxError } from './types';
|
|
11
|
+
declare class Synx {
|
|
12
|
+
static parse<T extends SynxObject = SynxObject>(text: string, options?: SynxOptions): T;
|
|
13
|
+
static stringify(obj: SynxObject, active?: boolean): string;
|
|
14
|
+
}
|
|
15
|
+
export default Synx;
|
|
16
|
+
export { Synx };
|
|
17
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEvD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5F,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,cAAM,IAAI;IACR,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,CAAC;IAQ3F,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,UAAQ,GAAG,MAAM;CAQ1D;AAuCD,eAAe,IAAI,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,CAAC"}
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SYNX Browser Bundle — @aperturesyndicate/synx
|
|
4
|
+
*
|
|
5
|
+
* Lightweight browser-compatible build.
|
|
6
|
+
* No Node.js dependencies (fs, path).
|
|
7
|
+
* Provides: parse, stringify.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.Synx = exports.SynxError = void 0;
|
|
11
|
+
const parser_1 = require("./parser");
|
|
12
|
+
const engine_1 = require("./engine");
|
|
13
|
+
var types_1 = require("./types");
|
|
14
|
+
Object.defineProperty(exports, "SynxError", { enumerable: true, get: function () { return types_1.SynxError; } });
|
|
15
|
+
class Synx {
|
|
16
|
+
static parse(text, options = {}) {
|
|
17
|
+
const { root, mode } = (0, parser_1.parseData)(text);
|
|
18
|
+
if (mode === 'active') {
|
|
19
|
+
(0, engine_1.resolve)(root, options);
|
|
20
|
+
}
|
|
21
|
+
return root;
|
|
22
|
+
}
|
|
23
|
+
static stringify(obj, active = false) {
|
|
24
|
+
let out = '';
|
|
25
|
+
if (active) {
|
|
26
|
+
out += '!active\n';
|
|
27
|
+
}
|
|
28
|
+
out += serializeObject(obj, 0);
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.Synx = Synx;
|
|
33
|
+
function serializeObject(obj, indent) {
|
|
34
|
+
let out = '';
|
|
35
|
+
const spaces = ' '.repeat(indent);
|
|
36
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
37
|
+
if (Array.isArray(val)) {
|
|
38
|
+
out += `${spaces}${key}\n`;
|
|
39
|
+
for (const item of val) {
|
|
40
|
+
if (item && typeof item === 'object' && !Array.isArray(item)) {
|
|
41
|
+
const entries = Object.entries(item);
|
|
42
|
+
if (entries.length > 0) {
|
|
43
|
+
const [firstKey, firstVal] = entries[0];
|
|
44
|
+
out += `${spaces} - ${firstKey} ${firstVal}\n`;
|
|
45
|
+
for (let i = 1; i < entries.length; i++) {
|
|
46
|
+
out += `${spaces} ${entries[i][0]} ${entries[i][1]}\n`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
out += `${spaces} - ${item}\n`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (val && typeof val === 'object') {
|
|
56
|
+
out += `${spaces}${key}\n`;
|
|
57
|
+
out += serializeObject(val, indent + 2);
|
|
58
|
+
}
|
|
59
|
+
else if (typeof val === 'string' && val.includes('\n')) {
|
|
60
|
+
out += `${spaces}${key} |\n`;
|
|
61
|
+
for (const line of val.split('\n')) {
|
|
62
|
+
out += `${spaces} ${line}\n`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
out += `${spaces}${key} ${val}\n`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
exports.default = Synx;
|
|
72
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
package/dist/calc.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SYNX Safe Calculator — @aperturesyndicate/synx
|
|
3
|
+
*
|
|
4
|
+
* Evaluates arithmetic expressions WITHOUT eval() or new Function().
|
|
5
|
+
* Supports: +, -, *, /, %, parentheses, and numeric literals.
|
|
6
|
+
* Variable references are resolved before this stage.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Safely evaluate an arithmetic expression.
|
|
10
|
+
* All variable names must be substituted with numbers before calling this.
|
|
11
|
+
*
|
|
12
|
+
* @param expr - e.g. "100 * 5 + (20 / 4)"
|
|
13
|
+
* @returns The computed number
|
|
14
|
+
*/
|
|
15
|
+
export declare function safeCalc(expr: string): number;
|
|
16
|
+
//# sourceMappingURL=calc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calc.d.ts","sourceRoot":"","sources":["../src/calc.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8IH;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7C"}
|
package/dist/calc.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SYNX Safe Calculator — @aperturesyndicate/synx
|
|
4
|
+
*
|
|
5
|
+
* Evaluates arithmetic expressions WITHOUT eval() or new Function().
|
|
6
|
+
* Supports: +, -, *, /, %, parentheses, and numeric literals.
|
|
7
|
+
* Variable references are resolved before this stage.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.safeCalc = safeCalc;
|
|
11
|
+
/**
|
|
12
|
+
* Tokenize an arithmetic expression string.
|
|
13
|
+
*/
|
|
14
|
+
function tokenize(expr) {
|
|
15
|
+
const tokens = [];
|
|
16
|
+
let i = 0;
|
|
17
|
+
while (i < expr.length) {
|
|
18
|
+
const ch = expr[i];
|
|
19
|
+
// Skip whitespace
|
|
20
|
+
if (ch === ' ' || ch === '\t') {
|
|
21
|
+
i++;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
// Number (integer or float, including negative after operator/start)
|
|
25
|
+
if ((ch >= '0' && ch <= '9') ||
|
|
26
|
+
(ch === '.' && i + 1 < expr.length && expr[i + 1] >= '0' && expr[i + 1] <= '9') ||
|
|
27
|
+
(ch === '-' && (tokens.length === 0 || tokens[tokens.length - 1].type === 'op' || (tokens[tokens.length - 1].type === 'paren' && tokens[tokens.length - 1].value === '(')))) {
|
|
28
|
+
let num = '';
|
|
29
|
+
if (ch === '-') {
|
|
30
|
+
num += '-';
|
|
31
|
+
i++;
|
|
32
|
+
}
|
|
33
|
+
while (i < expr.length && ((expr[i] >= '0' && expr[i] <= '9') || expr[i] === '.')) {
|
|
34
|
+
num += expr[i];
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
tokens.push({ type: 'number', value: parseFloat(num) });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
// Operators
|
|
41
|
+
if ('+-*/%'.includes(ch)) {
|
|
42
|
+
tokens.push({ type: 'op', value: ch });
|
|
43
|
+
i++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
// Parentheses
|
|
47
|
+
if (ch === '(' || ch === ')') {
|
|
48
|
+
tokens.push({ type: 'paren', value: ch });
|
|
49
|
+
i++;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`SYNX :calc — unexpected character: '${ch}' in expression "${expr}"`);
|
|
53
|
+
}
|
|
54
|
+
return tokens;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Recursive descent parser for arithmetic.
|
|
58
|
+
* Grammar:
|
|
59
|
+
* expr → term (('+' | '-') term)*
|
|
60
|
+
* term → factor (('*' | '/' | '%') factor)*
|
|
61
|
+
* factor → NUMBER | '(' expr ')'
|
|
62
|
+
*/
|
|
63
|
+
class ExprParser {
|
|
64
|
+
constructor(tokens) {
|
|
65
|
+
this.tokens = tokens;
|
|
66
|
+
this.pos = 0;
|
|
67
|
+
}
|
|
68
|
+
parse() {
|
|
69
|
+
const result = this.expr();
|
|
70
|
+
if (this.pos < this.tokens.length) {
|
|
71
|
+
throw new Error(`SYNX :calc — unexpected token at position ${this.pos}`);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
expr() {
|
|
76
|
+
let left = this.term();
|
|
77
|
+
while (this.pos < this.tokens.length && this.tokens[this.pos].type === 'op' && (this.tokens[this.pos].value === '+' || this.tokens[this.pos].value === '-')) {
|
|
78
|
+
const op = this.tokens[this.pos].value;
|
|
79
|
+
this.pos++;
|
|
80
|
+
const right = this.term();
|
|
81
|
+
left = op === '+' ? left + right : left - right;
|
|
82
|
+
}
|
|
83
|
+
return left;
|
|
84
|
+
}
|
|
85
|
+
term() {
|
|
86
|
+
let left = this.factor();
|
|
87
|
+
while (this.pos < this.tokens.length && this.tokens[this.pos].type === 'op' && ('*/%'.includes(this.tokens[this.pos].value))) {
|
|
88
|
+
const op = this.tokens[this.pos].value;
|
|
89
|
+
this.pos++;
|
|
90
|
+
const right = this.factor();
|
|
91
|
+
if (op === '*')
|
|
92
|
+
left = left * right;
|
|
93
|
+
else if (op === '/') {
|
|
94
|
+
if (right === 0)
|
|
95
|
+
throw new Error('SYNX :calc — division by zero');
|
|
96
|
+
left = left / right;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
if (right === 0)
|
|
100
|
+
throw new Error('SYNX :calc — division by zero');
|
|
101
|
+
left = left % right;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return left;
|
|
105
|
+
}
|
|
106
|
+
factor() {
|
|
107
|
+
const tok = this.tokens[this.pos];
|
|
108
|
+
if (!tok) {
|
|
109
|
+
throw new Error('SYNX :calc — unexpected end of expression');
|
|
110
|
+
}
|
|
111
|
+
if (tok.type === 'number') {
|
|
112
|
+
this.pos++;
|
|
113
|
+
return tok.value;
|
|
114
|
+
}
|
|
115
|
+
if (tok.type === 'paren' && tok.value === '(') {
|
|
116
|
+
this.pos++; // skip '('
|
|
117
|
+
const val = this.expr();
|
|
118
|
+
if (!this.tokens[this.pos] || this.tokens[this.pos].type !== 'paren' || this.tokens[this.pos].value !== ')') {
|
|
119
|
+
throw new Error('SYNX :calc — missing closing parenthesis');
|
|
120
|
+
}
|
|
121
|
+
this.pos++; // skip ')'
|
|
122
|
+
return val;
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`SYNX :calc — unexpected token: ${JSON.stringify(tok)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Safely evaluate an arithmetic expression.
|
|
129
|
+
* All variable names must be substituted with numbers before calling this.
|
|
130
|
+
*
|
|
131
|
+
* @param expr - e.g. "100 * 5 + (20 / 4)"
|
|
132
|
+
* @returns The computed number
|
|
133
|
+
*/
|
|
134
|
+
function safeCalc(expr) {
|
|
135
|
+
const tokens = tokenize(expr.trim());
|
|
136
|
+
if (tokens.length === 0)
|
|
137
|
+
return 0;
|
|
138
|
+
return new ExprParser(tokens).parse();
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=calc.js.map
|
package/dist/calc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calc.js","sourceRoot":"","sources":["../src/calc.ts"],"names":[],"mappings":"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"}
|