code-the-jewels 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.
Files changed (60) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/PROMPTS/01-build-v0.1.md +10 -0
  3. package/README.md +186 -0
  4. package/dist/ast.d.ts +143 -0
  5. package/dist/ast.js +2 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +145 -0
  8. package/dist/diagnostics.d.ts +7 -0
  9. package/dist/diagnostics.js +16 -0
  10. package/dist/generator.d.ts +11 -0
  11. package/dist/generator.js +126 -0
  12. package/dist/index.d.ts +7 -0
  13. package/dist/index.js +15 -0
  14. package/dist/lexer.d.ts +18 -0
  15. package/dist/lexer.js +210 -0
  16. package/dist/parser.d.ts +40 -0
  17. package/dist/parser.js +394 -0
  18. package/dist/repl.d.ts +1 -0
  19. package/dist/repl.js +132 -0
  20. package/dist/runtime/atl-data.d.ts +4 -0
  21. package/dist/runtime/atl-data.js +18 -0
  22. package/dist/runtime/atl-flow.d.ts +1 -0
  23. package/dist/runtime/atl-flow.js +5 -0
  24. package/dist/runtime/bk-parse.d.ts +3 -0
  25. package/dist/runtime/bk-parse.js +9 -0
  26. package/dist/runtime/bk-text.d.ts +5 -0
  27. package/dist/runtime/bk-text.js +13 -0
  28. package/dist/runtime/rtj-core.d.ts +1 -0
  29. package/dist/runtime/rtj-core.js +51 -0
  30. package/dist/semantic.d.ts +11 -0
  31. package/dist/semantic.js +153 -0
  32. package/dist/tests/basic.test.d.ts +1 -0
  33. package/dist/tests/basic.test.js +69 -0
  34. package/dist/token.d.ts +56 -0
  35. package/dist/token.js +77 -0
  36. package/examples/cities.rtj +11 -0
  37. package/examples/count-words.rtj +12 -0
  38. package/examples/duo.rtj +12 -0
  39. package/examples/hello.rtj +1 -0
  40. package/examples/pipes.rtj +6 -0
  41. package/package.json +22 -0
  42. package/public/_redirects +1 -0
  43. package/public/index.html +559 -0
  44. package/src/ast.ts +189 -0
  45. package/src/cli.ts +120 -0
  46. package/src/diagnostics.ts +15 -0
  47. package/src/generator.ts +129 -0
  48. package/src/index.ts +7 -0
  49. package/src/lexer.ts +208 -0
  50. package/src/parser.ts +461 -0
  51. package/src/repl.ts +105 -0
  52. package/src/runtime/atl-data.ts +11 -0
  53. package/src/runtime/atl-flow.ts +1 -0
  54. package/src/runtime/bk-parse.ts +3 -0
  55. package/src/runtime/bk-text.ts +5 -0
  56. package/src/runtime/rtj-core.ts +21 -0
  57. package/src/semantic.ts +144 -0
  58. package/src/tests/basic.test.ts +74 -0
  59. package/src/token.ts +85 -0
  60. package/tsconfig.json +15 -0
@@ -0,0 +1,144 @@
1
+ import { Program, Statement, Expression } from './ast';
2
+ import { RTJError } from './diagnostics';
3
+
4
+ const VALID_MODULES = ['bk:text', 'bk:parse', 'atl:data', 'atl:flow'];
5
+
6
+ export class Semantic {
7
+ private scopes: Set<string>[] = [];
8
+
9
+ analyze(program: Program): void {
10
+ this.pushScope();
11
+ for (const stmt of program.body) {
12
+ this.analyzeStatement(stmt);
13
+ }
14
+ this.popScope();
15
+ }
16
+
17
+ private pushScope(): void {
18
+ this.scopes.push(new Set());
19
+ }
20
+
21
+ private popScope(): void {
22
+ this.scopes.pop();
23
+ }
24
+
25
+ private declare(name: string, line?: number): void {
26
+ const top = this.scopes[this.scopes.length - 1];
27
+ if (top.has(name)) {
28
+ throw new RTJError('NameError', `duplicate jewel '${name}'`, line);
29
+ }
30
+ top.add(name);
31
+ }
32
+
33
+ private lookup(name: string): boolean {
34
+ for (let i = this.scopes.length - 1; i >= 0; i--) {
35
+ if (this.scopes[i].has(name)) return true;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ private analyzeStatement(stmt: Statement): void {
41
+ switch (stmt.type) {
42
+ case 'VarDecl':
43
+ this.analyzeExpression(stmt.init);
44
+ this.declare(stmt.name, stmt.loc.line);
45
+ break;
46
+ case 'FunctionDecl':
47
+ this.declare(stmt.name, stmt.loc.line);
48
+ this.pushScope();
49
+ for (const p of stmt.params) {
50
+ this.declare(p, stmt.loc.line);
51
+ }
52
+ for (const s of stmt.body.body) {
53
+ this.analyzeStatement(s);
54
+ }
55
+ this.popScope();
56
+ break;
57
+ case 'ReturnStmt':
58
+ if (stmt.value) this.analyzeExpression(stmt.value);
59
+ break;
60
+ case 'TalkStmt':
61
+ this.analyzeExpression(stmt.value);
62
+ break;
63
+ case 'IfStmt':
64
+ this.analyzeExpression(stmt.condition);
65
+ this.pushScope();
66
+ for (const s of stmt.consequent.body) this.analyzeStatement(s);
67
+ this.popScope();
68
+ if (stmt.alternate) {
69
+ this.pushScope();
70
+ for (const s of stmt.alternate.body) this.analyzeStatement(s);
71
+ this.popScope();
72
+ }
73
+ break;
74
+ case 'LoopStmt':
75
+ this.analyzeExpression(stmt.iterable);
76
+ this.pushScope();
77
+ this.declare(stmt.variable, stmt.loc.line);
78
+ for (const s of stmt.body.body) this.analyzeStatement(s);
79
+ this.popScope();
80
+ break;
81
+ case 'ImportStmt':
82
+ if (!VALID_MODULES.includes(stmt.source)) {
83
+ throw new RTJError('ImportError', `unknown feature source '${stmt.source}'`, stmt.loc.line);
84
+ }
85
+ for (const name of stmt.names) {
86
+ this.declare(name, stmt.loc.line);
87
+ }
88
+ break;
89
+ case 'ThrowStmt':
90
+ this.analyzeExpression(stmt.value);
91
+ break;
92
+ case 'ExpressionStmt':
93
+ this.analyzeExpression(stmt.expr);
94
+ break;
95
+ case 'BlockStmt':
96
+ this.pushScope();
97
+ for (const s of stmt.body) this.analyzeStatement(s);
98
+ this.popScope();
99
+ break;
100
+ }
101
+ }
102
+
103
+ private analyzeExpression(expr: Expression): void {
104
+ switch (expr.type) {
105
+ case 'Identifier':
106
+ // Warn but don't error in v0.1 (runtime may provide)
107
+ break;
108
+ case 'StringLiteral':
109
+ case 'NumberLiteral':
110
+ case 'BooleanLiteral':
111
+ case 'NullLiteral':
112
+ break;
113
+ case 'ArrayLiteral':
114
+ for (const el of expr.elements) this.analyzeExpression(el);
115
+ break;
116
+ case 'ObjectLiteral':
117
+ for (const pair of expr.pairs) this.analyzeExpression(pair.value);
118
+ break;
119
+ case 'BinaryExpr':
120
+ this.analyzeExpression(expr.left);
121
+ this.analyzeExpression(expr.right);
122
+ break;
123
+ case 'UnaryExpr':
124
+ this.analyzeExpression(expr.operand);
125
+ break;
126
+ case 'CallExpr':
127
+ this.analyzeExpression(expr.callee);
128
+ for (const arg of expr.args) this.analyzeExpression(arg);
129
+ break;
130
+ case 'MemberExpr':
131
+ this.analyzeExpression(expr.object);
132
+ if (expr.computed) this.analyzeExpression(expr.property);
133
+ break;
134
+ case 'PipeExpr':
135
+ for (const step of expr.steps) this.analyzeExpression(step);
136
+ break;
137
+ case 'DuoExpr':
138
+ this.analyzeExpression(expr.input);
139
+ for (const step of expr.mikePipeline) this.analyzeExpression(step);
140
+ for (const step of expr.elPipeline) this.analyzeExpression(step);
141
+ break;
142
+ }
143
+ }
144
+ }
@@ -0,0 +1,74 @@
1
+ import { Lexer } from '../lexer';
2
+ import { Parser } from '../parser';
3
+ import { Semantic } from '../semantic';
4
+ import { Generator } from '../generator';
5
+
6
+ function compile(source: string): string {
7
+ const lexer = new Lexer(source);
8
+ const tokens = lexer.tokenize();
9
+ const parser = new Parser(tokens);
10
+ const ast = parser.parse();
11
+ const semantic = new Semantic();
12
+ semantic.analyze(ast);
13
+ const gen = new Generator();
14
+ return gen.generate(ast);
15
+ }
16
+
17
+ function test(name: string, fn: () => void): void {
18
+ try {
19
+ fn();
20
+ console.log(`PASS: ${name}`);
21
+ } catch (err) {
22
+ console.error(`FAIL: ${name}`);
23
+ console.error(err);
24
+ process.exitCode = 1;
25
+ }
26
+ }
27
+
28
+ function assert(condition: boolean, msg: string): void {
29
+ if (!condition) throw new Error(`Assertion failed: ${msg}`);
30
+ }
31
+
32
+ test('lexer tokenizes hello world', () => {
33
+ const lexer = new Lexer('talk "hello"');
34
+ const tokens = lexer.tokenize();
35
+ assert(tokens[0].type === 'TALK', 'first token is TALK');
36
+ assert(tokens[1].type === 'STRING', 'second token is STRING');
37
+ assert(tokens[1].value === 'hello', 'string value is hello');
38
+ });
39
+
40
+ test('parser creates TalkStmt', () => {
41
+ const lexer = new Lexer('talk "hello"');
42
+ const tokens = lexer.tokenize();
43
+ const parser = new Parser(tokens);
44
+ const ast = parser.parse();
45
+ assert(ast.body.length === 1, 'one statement');
46
+ assert(ast.body[0].type === 'TalkStmt', 'is TalkStmt');
47
+ });
48
+
49
+ test('generator outputs talk call', () => {
50
+ const js = compile('talk "hello"');
51
+ assert(js.includes('__rtj.talk("hello")'), 'contains talk call');
52
+ });
53
+
54
+ test('pipe expression compiles to nested calls', () => {
55
+ const js = compile('feature trim, upper from "bk:text"\njewel x = "hi" |> trim |> upper');
56
+ assert(js.includes('upper(trim("hi"))'), 'pipe flattened correctly');
57
+ });
58
+
59
+ test('duo expression compiles', () => {
60
+ const js = compile('feature trim from "bk:text"\nfeature words from "bk:parse"\nfeature count from "atl:data"\njewel x = duo("hi") {\n mike: trim |> words\n el: count\n}');
61
+ assert(js.includes('count(words(trim("hi")))'), 'duo flattened correctly');
62
+ });
63
+
64
+ test('semantic rejects invalid import source', () => {
65
+ let threw = false;
66
+ try {
67
+ compile('feature foo from "bk:audio"');
68
+ } catch (err) {
69
+ threw = true;
70
+ }
71
+ assert(threw, 'should throw on invalid import');
72
+ });
73
+
74
+ console.log('All tests complete.');
package/src/token.ts ADDED
@@ -0,0 +1,85 @@
1
+ export enum TokenType {
2
+ // Keywords
3
+ JEWEL = 'JEWEL',
4
+ VERSE = 'VERSE',
5
+ SEND = 'SEND',
6
+ TALK = 'TALK',
7
+ IFWILD = 'IFWILD',
8
+ ELSEWILD = 'ELSEWILD',
9
+ RUN = 'RUN',
10
+ IN = 'IN',
11
+ FEATURE = 'FEATURE',
12
+ FROM = 'FROM',
13
+ YANK = 'YANK',
14
+ DUO = 'DUO',
15
+ MIKE = 'MIKE',
16
+ EL = 'EL',
17
+
18
+ // Literals
19
+ IDENTIFIER = 'IDENTIFIER',
20
+ STRING = 'STRING',
21
+ NUMBER = 'NUMBER',
22
+ BOOLEAN = 'BOOLEAN',
23
+ NULL = 'NULL',
24
+
25
+ // Operators
26
+ PIPE = 'PIPE',
27
+ PLUS = 'PLUS',
28
+ MINUS = 'MINUS',
29
+ STAR = 'STAR',
30
+ SLASH = 'SLASH',
31
+ PERCENT = 'PERCENT',
32
+ EQ_EQ = 'EQ_EQ',
33
+ BANG_EQ = 'BANG_EQ',
34
+ GT = 'GT',
35
+ LT = 'LT',
36
+ GT_EQ = 'GT_EQ',
37
+ LT_EQ = 'LT_EQ',
38
+ AND_AND = 'AND_AND',
39
+ OR_OR = 'OR_OR',
40
+ BANG = 'BANG',
41
+ ASSIGN = 'ASSIGN',
42
+
43
+ // Punctuation
44
+ LBRACE = 'LBRACE',
45
+ RBRACE = 'RBRACE',
46
+ LPAREN = 'LPAREN',
47
+ RPAREN = 'RPAREN',
48
+ LBRACKET = 'LBRACKET',
49
+ RBRACKET = 'RBRACKET',
50
+ COMMA = 'COMMA',
51
+ COLON = 'COLON',
52
+ DOT = 'DOT',
53
+ SEMICOLON = 'SEMICOLON',
54
+
55
+ // Special
56
+ NEWLINE = 'NEWLINE',
57
+ EOF = 'EOF',
58
+ }
59
+
60
+ export interface Token {
61
+ type: TokenType;
62
+ value: string;
63
+ line: number;
64
+ column: number;
65
+ }
66
+
67
+ export const KEYWORDS: Record<string, TokenType> = {
68
+ jewel: TokenType.JEWEL,
69
+ verse: TokenType.VERSE,
70
+ send: TokenType.SEND,
71
+ talk: TokenType.TALK,
72
+ ifwild: TokenType.IFWILD,
73
+ elsewild: TokenType.ELSEWILD,
74
+ run: TokenType.RUN,
75
+ in: TokenType.IN,
76
+ feature: TokenType.FEATURE,
77
+ from: TokenType.FROM,
78
+ yank: TokenType.YANK,
79
+ duo: TokenType.DUO,
80
+ mike: TokenType.MIKE,
81
+ el: TokenType.EL,
82
+ true: TokenType.BOOLEAN,
83
+ false: TokenType.BOOLEAN,
84
+ null: TokenType.NULL,
85
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "types": ["node"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "declaration": true,
12
+ "resolveJsonModule": true
13
+ },
14
+ "include": ["src/**/*"]
15
+ }