english-lang 0.1.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/README.md +119 -0
- package/dist/cli/engc.js +198 -0
- package/dist/index.js +20 -0
- package/dist/src/ast/ASTNodes.js +6 -0
- package/dist/src/codegen/ReactNativeBackend.js +657 -0
- package/dist/src/lexer/Lexer.js +167 -0
- package/dist/src/parser/Parser.js +1165 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# English (.eng)
|
|
2
|
+
|
|
3
|
+
> **Write what you mean. Build what you know.**
|
|
4
|
+
|
|
5
|
+
A programming language where you write plain English that compiles directly to React Native — shipping real iOS and Android apps with zero JSX, zero hooks, and zero accidental complexity.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g english-lang
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Compile a single file
|
|
21
|
+
engc screens/HomeScreen.eng
|
|
22
|
+
|
|
23
|
+
# Compile a single file to a specific output directory
|
|
24
|
+
engc screens/HomeScreen.eng --out rn/src/
|
|
25
|
+
|
|
26
|
+
# Compile all .eng files in a directory
|
|
27
|
+
engc compile screens/ --out rn/src/
|
|
28
|
+
|
|
29
|
+
# Watch a directory and recompile on every save
|
|
30
|
+
engc watch screens/ --out rn/src/
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Example
|
|
36
|
+
|
|
37
|
+
Write this in `screens/HomeScreen.eng`:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
screen HomeScreen:
|
|
41
|
+
title is "Hello World"
|
|
42
|
+
|
|
43
|
+
state:
|
|
44
|
+
message is "Hello!"
|
|
45
|
+
count is 0
|
|
46
|
+
|
|
47
|
+
when screen opens:
|
|
48
|
+
set count to 1
|
|
49
|
+
|
|
50
|
+
when "Say Hello" is pressed:
|
|
51
|
+
set message to "Hello from English!"
|
|
52
|
+
|
|
53
|
+
layout:
|
|
54
|
+
vertical stack spacing 16 padding 16:
|
|
55
|
+
show message as heading
|
|
56
|
+
show count
|
|
57
|
+
button "Say Hello"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Run `engc screens/HomeScreen.eng --out rn/src/` and get this:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
export function HomeScreen({ navigation }: any) {
|
|
64
|
+
const [message, setMessage] = useState<string>("Hello!");
|
|
65
|
+
const [count, setCount] = useState<number>(0);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setCount(1);
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
const handleSayHello = () => {
|
|
72
|
+
setMessage("Hello from English!");
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<View style={styles.container}>
|
|
77
|
+
<View style={{ flexDirection: 'column', gap: 16, padding: 16 }}>
|
|
78
|
+
<Text style={styles.heading}>{message}</Text>
|
|
79
|
+
<Text style={styles.body}>{count}</Text>
|
|
80
|
+
<TouchableOpacity style={styles.button} onPress={handleSayHello}>
|
|
81
|
+
<Text style={styles.buttonText}>Say Hello</Text>
|
|
82
|
+
</TouchableOpacity>
|
|
83
|
+
</View>
|
|
84
|
+
</View>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Project structure
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
my-app/
|
|
95
|
+
├── package.json ← devDependency: english-lang
|
|
96
|
+
├── screens/ ← write your .eng files here
|
|
97
|
+
│ ├── HomeScreen.eng
|
|
98
|
+
│ └── ProductScreen.eng
|
|
99
|
+
└── rn/ ← react native project
|
|
100
|
+
├── src/ ← compiled .tsx files go here
|
|
101
|
+
├── ios/
|
|
102
|
+
└── android/
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Programmatic use
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { compile } from 'english-lang';
|
|
111
|
+
|
|
112
|
+
const tsx = compile(engSource, 'HomeScreen.eng');
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT
|
package/dist/cli/engc.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// ============================================================
|
|
4
|
+
// engc — English (.eng) compiler CLI
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// engc <file.eng> compile → .tsx alongside source
|
|
8
|
+
// engc <file.eng> --out <dir> compile → specific output dir
|
|
9
|
+
// engc <file.eng> -o <output.tsx> compile → specific output file
|
|
10
|
+
// engc <file.eng> --print print generated code to stdout
|
|
11
|
+
// engc compile <dir> --out <dir> compile all .eng files in a dir
|
|
12
|
+
// engc watch <dir> --out <dir> watch dir, recompile on save
|
|
13
|
+
// ============================================================
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const index_1 = require("../index");
|
|
51
|
+
// ── Helpers ───────────────────────────────────────────────────
|
|
52
|
+
function compileFile(inputPath, outDir) {
|
|
53
|
+
const source = fs.readFileSync(inputPath, 'utf8');
|
|
54
|
+
const sourceFile = path.basename(inputPath);
|
|
55
|
+
let tsx;
|
|
56
|
+
try {
|
|
57
|
+
tsx = (0, index_1.compile)(source, sourceFile);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.error(`[engc] Parse error in ${sourceFile}:\n ${err.message}\n`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const outputPath = outDir
|
|
64
|
+
? path.join(outDir, sourceFile.replace(/\.eng$/, '.tsx'))
|
|
65
|
+
: inputPath.replace(/\.eng$/, '.tsx');
|
|
66
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
67
|
+
fs.writeFileSync(outputPath, tsx, 'utf8');
|
|
68
|
+
const relIn = path.relative(process.cwd(), inputPath);
|
|
69
|
+
const relOut = path.relative(process.cwd(), outputPath);
|
|
70
|
+
console.log(`[engc] ${relIn} → ${relOut}`);
|
|
71
|
+
}
|
|
72
|
+
function compileDir(srcDir, outDir) {
|
|
73
|
+
const engFiles = fs.readdirSync(srcDir).filter(f => f.endsWith('.eng'));
|
|
74
|
+
if (engFiles.length === 0) {
|
|
75
|
+
console.log(`[engc] No .eng files found in ${srcDir}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
for (const file of engFiles) {
|
|
79
|
+
compileFile(path.join(srcDir, file), outDir);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function watchDir(srcDir, outDir) {
|
|
83
|
+
// Run once on start
|
|
84
|
+
compileDir(srcDir, outDir);
|
|
85
|
+
console.log(`[engc] Watching ${srcDir} ...`);
|
|
86
|
+
fs.watch(srcDir, { recursive: false }, (event, filename) => {
|
|
87
|
+
if (!filename || !filename.endsWith('.eng'))
|
|
88
|
+
return;
|
|
89
|
+
const filePath = path.join(srcDir, filename);
|
|
90
|
+
if (!fs.existsSync(filePath))
|
|
91
|
+
return;
|
|
92
|
+
compileFile(filePath, outDir);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// ── CLI entry point ───────────────────────────────────────────
|
|
96
|
+
function main() {
|
|
97
|
+
const args = process.argv.slice(2);
|
|
98
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
99
|
+
console.log(`
|
|
100
|
+
English Compiler (engc)
|
|
101
|
+
|
|
102
|
+
Usage:
|
|
103
|
+
engc <file.eng> compile → .tsx alongside source
|
|
104
|
+
engc <file.eng> --out <dir> compile → specific output directory
|
|
105
|
+
engc <file.eng> -o <output.tsx> compile → specific output file
|
|
106
|
+
engc <file.eng> --print print generated code, no file written
|
|
107
|
+
engc <file.eng> --tokens dump token stream (debug)
|
|
108
|
+
engc <file.eng> --ast dump parsed AST (debug)
|
|
109
|
+
engc compile <dir> [--out <dir>] compile all .eng files in a directory
|
|
110
|
+
engc watch <dir> [--out <dir>] watch directory, recompile on save
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
engc screens/HomeScreen.eng
|
|
114
|
+
engc screens/HomeScreen.eng --out rn/src/
|
|
115
|
+
engc compile screens/ --out rn/src/
|
|
116
|
+
engc watch screens/ --out rn/src/
|
|
117
|
+
`.trim());
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}
|
|
120
|
+
// Sub-commands: compile <dir> | watch <dir>
|
|
121
|
+
if (args[0] === 'compile' || args[0] === 'watch') {
|
|
122
|
+
const cmd = args[0];
|
|
123
|
+
const srcDir = args[1] ? path.resolve(args[1]) : process.cwd();
|
|
124
|
+
const outIdx = args.indexOf('--out');
|
|
125
|
+
const outDir = outIdx !== -1 ? path.resolve(args[outIdx + 1]) : undefined;
|
|
126
|
+
if (!fs.existsSync(srcDir)) {
|
|
127
|
+
console.error(`[engc] Directory not found: ${srcDir}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
if (cmd === 'compile') {
|
|
131
|
+
compileDir(srcDir, outDir);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
watchDir(srcDir, outDir);
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Single file mode
|
|
139
|
+
const inputArg = args[0];
|
|
140
|
+
const printOnly = args.includes('--print');
|
|
141
|
+
const dumpTokens = args.includes('--tokens');
|
|
142
|
+
const dumpAST = args.includes('--ast');
|
|
143
|
+
const outDirIdx = args.indexOf('--out');
|
|
144
|
+
const outDir = outDirIdx !== -1 ? args[outDirIdx + 1] : undefined;
|
|
145
|
+
const outputIdx = args.indexOf('-o');
|
|
146
|
+
const explicitOutput = outputIdx !== -1 ? args[outputIdx + 1] : null;
|
|
147
|
+
const inputPath = path.resolve(inputArg);
|
|
148
|
+
if (!fs.existsSync(inputPath)) {
|
|
149
|
+
console.error(`[engc] File not found: ${inputPath}`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
const source = fs.readFileSync(inputPath, 'utf8');
|
|
153
|
+
const sourceFile = path.basename(inputPath);
|
|
154
|
+
// Debug modes — need raw Lexer/Parser
|
|
155
|
+
if (dumpTokens || dumpAST) {
|
|
156
|
+
const { Lexer } = require('../src/lexer/Lexer');
|
|
157
|
+
const { Parser } = require('../src/parser/Parser');
|
|
158
|
+
const tokens = new Lexer(source).tokenize();
|
|
159
|
+
if (dumpTokens) {
|
|
160
|
+
console.log('── Token stream ──────────────────────────────────────');
|
|
161
|
+
for (const tok of tokens) {
|
|
162
|
+
const val = tok.value ? ` "${tok.value}"` : '';
|
|
163
|
+
console.log(` ${String(tok.line).padStart(3)}:${String(tok.col).padStart(3)} ${tok.type.padEnd(14)}${val}`);
|
|
164
|
+
}
|
|
165
|
+
console.log('');
|
|
166
|
+
}
|
|
167
|
+
if (dumpAST) {
|
|
168
|
+
const program = new Parser(tokens).parse();
|
|
169
|
+
console.log('── AST ───────────────────────────────────────────────');
|
|
170
|
+
console.log(JSON.stringify(program, null, 2));
|
|
171
|
+
console.log('');
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
let tsx;
|
|
176
|
+
try {
|
|
177
|
+
tsx = (0, index_1.compile)(source, sourceFile);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
console.error(`\n[engc] Parse error:\n ${err.message}\n`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
if (printOnly) {
|
|
184
|
+
console.log(tsx);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const outputPath = explicitOutput
|
|
188
|
+
? path.resolve(explicitOutput)
|
|
189
|
+
: outDir
|
|
190
|
+
? path.join(path.resolve(outDir), sourceFile.replace(/\.eng$/, '.tsx'))
|
|
191
|
+
: inputPath.replace(/\.eng$/, '.tsx');
|
|
192
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
193
|
+
fs.writeFileSync(outputPath, tsx, 'utf8');
|
|
194
|
+
const relIn = path.relative(process.cwd(), inputPath);
|
|
195
|
+
const relOut = path.relative(process.cwd(), outputPath);
|
|
196
|
+
console.log(`[engc] ${relIn} → ${relOut}`);
|
|
197
|
+
}
|
|
198
|
+
main();
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// english-lang — public API
|
|
3
|
+
// Import this if you want to use the compiler programmatically.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.ReactNativeBackend = exports.Parser = exports.Lexer = void 0;
|
|
6
|
+
exports.compile = compile;
|
|
7
|
+
var Lexer_1 = require("./src/lexer/Lexer");
|
|
8
|
+
Object.defineProperty(exports, "Lexer", { enumerable: true, get: function () { return Lexer_1.Lexer; } });
|
|
9
|
+
var Parser_1 = require("./src/parser/Parser");
|
|
10
|
+
Object.defineProperty(exports, "Parser", { enumerable: true, get: function () { return Parser_1.Parser; } });
|
|
11
|
+
var ReactNativeBackend_1 = require("./src/codegen/ReactNativeBackend");
|
|
12
|
+
Object.defineProperty(exports, "ReactNativeBackend", { enumerable: true, get: function () { return ReactNativeBackend_1.ReactNativeBackend; } });
|
|
13
|
+
const Lexer_2 = require("./src/lexer/Lexer");
|
|
14
|
+
const Parser_2 = require("./src/parser/Parser");
|
|
15
|
+
const ReactNativeBackend_2 = require("./src/codegen/ReactNativeBackend");
|
|
16
|
+
function compile(source, sourceFile) {
|
|
17
|
+
const tokens = new Lexer_2.Lexer(source).tokenize();
|
|
18
|
+
const program = new Parser_2.Parser(tokens).parse();
|
|
19
|
+
return new ReactNativeBackend_2.ReactNativeBackend().emit(program, sourceFile);
|
|
20
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// ASTNodes.ts — All node type definitions for the English compiler
|
|
4
|
+
// Every node carries a `kind` discriminant for exhaustive switch.
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|