freelang-v4 4.3.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 +548 -0
- package/dist/ast.d.ts +367 -0
- package/dist/ast.js +4 -0
- package/dist/ast.js.map +1 -0
- package/dist/async-basic.test.d.ts +1 -0
- package/dist/async-basic.test.js +88 -0
- package/dist/async-basic.test.js.map +1 -0
- package/dist/async-jest.test.d.ts +1 -0
- package/dist/async-jest.test.js +99 -0
- package/dist/async-jest.test.js.map +1 -0
- package/dist/channel-jest.test.d.ts +1 -0
- package/dist/channel-jest.test.js +148 -0
- package/dist/channel-jest.test.js.map +1 -0
- package/dist/checker-jest.test.d.ts +1 -0
- package/dist/checker-jest.test.js +160 -0
- package/dist/checker-jest.test.js.map +1 -0
- package/dist/checker.d.ts +149 -0
- package/dist/checker.js +1565 -0
- package/dist/checker.js.map +1 -0
- package/dist/checker.test.d.ts +1 -0
- package/dist/checker.test.js +217 -0
- package/dist/checker.test.js.map +1 -0
- package/dist/compiler-jest.test.d.ts +1 -0
- package/dist/compiler-jest.test.js +233 -0
- package/dist/compiler-jest.test.js.map +1 -0
- package/dist/compiler.d.ts +127 -0
- package/dist/compiler.js +1588 -0
- package/dist/compiler.js.map +1 -0
- package/dist/compiler.test.d.ts +1 -0
- package/dist/compiler.test.js +313 -0
- package/dist/compiler.test.js.map +1 -0
- package/dist/db-100m-full.d.ts +5 -0
- package/dist/db-100m-full.js +78 -0
- package/dist/db-100m-full.js.map +1 -0
- package/dist/db-100m-no-index.d.ts +12 -0
- package/dist/db-100m-no-index.js +119 -0
- package/dist/db-100m-no-index.js.map +1 -0
- package/dist/db-100m-real.d.ts +5 -0
- package/dist/db-100m-real.js +131 -0
- package/dist/db-100m-real.js.map +1 -0
- package/dist/db-100m-streaming.d.ts +15 -0
- package/dist/db-100m-streaming.js +164 -0
- package/dist/db-100m-streaming.js.map +1 -0
- package/dist/db-100m-test.d.ts +5 -0
- package/dist/db-100m-test.js +111 -0
- package/dist/db-100m-test.js.map +1 -0
- package/dist/db-jest.test.d.ts +1 -0
- package/dist/db-jest.test.js +182 -0
- package/dist/db-jest.test.js.map +1 -0
- package/dist/db-runtime.d.ts +24 -0
- package/dist/db-runtime.js +204 -0
- package/dist/db-runtime.js.map +1 -0
- package/dist/db.d.ts +249 -0
- package/dist/db.js +593 -0
- package/dist/db.js.map +1 -0
- package/dist/file-io-jest.test.d.ts +1 -0
- package/dist/file-io-jest.test.js +225 -0
- package/dist/file-io-jest.test.js.map +1 -0
- package/dist/for-of-jest.test.d.ts +1 -0
- package/dist/for-of-jest.test.js +230 -0
- package/dist/for-of-jest.test.js.map +1 -0
- package/dist/for-of.test.d.ts +1 -0
- package/dist/for-of.test.js +305 -0
- package/dist/for-of.test.js.map +1 -0
- package/dist/function-literal-jest.test.d.ts +1 -0
- package/dist/function-literal-jest.test.js +180 -0
- package/dist/function-literal-jest.test.js.map +1 -0
- package/dist/function-literal.test.d.ts +1 -0
- package/dist/function-literal.test.js +245 -0
- package/dist/function-literal.test.js.map +1 -0
- package/dist/generics-jest.test.d.ts +1 -0
- package/dist/generics-jest.test.js +93 -0
- package/dist/generics-jest.test.js.map +1 -0
- package/dist/ir-gen.d.ts +15 -0
- package/dist/ir-gen.js +400 -0
- package/dist/ir-gen.js.map +1 -0
- package/dist/ir.d.ts +114 -0
- package/dist/ir.js +5 -0
- package/dist/ir.js.map +1 -0
- package/dist/lexer.d.ts +110 -0
- package/dist/lexer.js +467 -0
- package/dist/lexer.js.map +1 -0
- package/dist/lexer.test.d.ts +1 -0
- package/dist/lexer.test.js +426 -0
- package/dist/lexer.test.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +241 -0
- package/dist/main.js.map +1 -0
- package/dist/module-jest.test.d.ts +1 -0
- package/dist/module-jest.test.js +123 -0
- package/dist/module-jest.test.js.map +1 -0
- package/dist/parser.d.ts +56 -0
- package/dist/parser.js +1060 -0
- package/dist/parser.js.map +1 -0
- package/dist/parser.test.d.ts +1 -0
- package/dist/parser.test.js +461 -0
- package/dist/parser.test.js.map +1 -0
- package/dist/pattern-matching-jest.test.d.ts +1 -0
- package/dist/pattern-matching-jest.test.js +158 -0
- package/dist/pattern-matching-jest.test.js.map +1 -0
- package/dist/pkg/init.d.ts +1 -0
- package/dist/pkg/init.js +118 -0
- package/dist/pkg/init.js.map +1 -0
- package/dist/pkg/install.d.ts +1 -0
- package/dist/pkg/install.js +77 -0
- package/dist/pkg/install.js.map +1 -0
- package/dist/pkg/registry.d.ts +23 -0
- package/dist/pkg/registry.js +106 -0
- package/dist/pkg/registry.js.map +1 -0
- package/dist/pkg/run.d.ts +1 -0
- package/dist/pkg/run.js +76 -0
- package/dist/pkg/run.js.map +1 -0
- package/dist/pkg/toml.d.ts +5 -0
- package/dist/pkg/toml.js +117 -0
- package/dist/pkg/toml.js.map +1 -0
- package/dist/repl.d.ts +15 -0
- package/dist/repl.js +197 -0
- package/dist/repl.js.map +1 -0
- package/dist/runtime/bytecode.d.ts +92 -0
- package/dist/runtime/bytecode.js +253 -0
- package/dist/runtime/bytecode.js.map +1 -0
- package/dist/runtime/value.d.ts +102 -0
- package/dist/runtime/value.js +302 -0
- package/dist/runtime/value.js.map +1 -0
- package/dist/runtime/vm.d.ts +65 -0
- package/dist/runtime/vm.js +293 -0
- package/dist/runtime/vm.js.map +1 -0
- package/dist/struct-instance-jest.test.d.ts +1 -0
- package/dist/struct-instance-jest.test.js +209 -0
- package/dist/struct-instance-jest.test.js.map +1 -0
- package/dist/struct-instance.test.d.ts +1 -0
- package/dist/struct-instance.test.js +291 -0
- package/dist/struct-instance.test.js.map +1 -0
- package/dist/struct-jest.test.d.ts +1 -0
- package/dist/struct-jest.test.js +176 -0
- package/dist/struct-jest.test.js.map +1 -0
- package/dist/struct.test.d.ts +1 -0
- package/dist/struct.test.js +231 -0
- package/dist/struct.test.js.map +1 -0
- package/dist/trait-jest.test.d.ts +1 -0
- package/dist/trait-jest.test.js +120 -0
- package/dist/trait-jest.test.js.map +1 -0
- package/dist/vm-jest.test.d.ts +1 -0
- package/dist/vm-jest.test.js +569 -0
- package/dist/vm-jest.test.js.map +1 -0
- package/dist/vm.d.ts +81 -0
- package/dist/vm.js +1956 -0
- package/dist/vm.js.map +1 -0
- package/dist/vm.test.d.ts +1 -0
- package/dist/vm.test.js +337 -0
- package/dist/vm.test.js.map +1 -0
- package/dist/web-repl/sandbox.d.ts +11 -0
- package/dist/web-repl/sandbox.js +76 -0
- package/dist/web-repl/sandbox.js.map +1 -0
- package/dist/web-repl/server.d.ts +1 -0
- package/dist/web-repl/server.js +111 -0
- package/dist/web-repl/server.js.map +1 -0
- package/dist/while-loop-jest.test.d.ts +1 -0
- package/dist/while-loop-jest.test.js +201 -0
- package/dist/while-loop-jest.test.js.map +1 -0
- package/dist/while-loop.test.d.ts +1 -0
- package/dist/while-loop.test.js +262 -0
- package/dist/while-loop.test.js.map +1 -0
- package/docs/EXPERIENCE.md +787 -0
- package/docs/README.md +175 -0
- package/docs/V1_V2_V3_ANALYSIS.md +107 -0
- package/docs/_config.yml +36 -0
- package/docs/api-reference.md +459 -0
- package/docs/architecture.md +470 -0
- package/docs/benchmarks.md +295 -0
- package/docs/comparison.md +454 -0
- package/docs/index.md +335 -0
- package/docs/language-completeness.md +228 -0
- package/docs/learning-guide.md +651 -0
- package/package.json +65 -0
- package/src/api/deploy_key.fl +294 -0
- package/src/api/issue.fl +302 -0
- package/src/api/org.fl +356 -0
- package/src/api/repo.fl +394 -0
- package/src/api/team.fl +299 -0
- package/src/api/user.fl +385 -0
- package/src/api/webhook.fl +273 -0
- package/src/ast.ts +158 -0
- package/src/async-basic.test.ts +94 -0
- package/src/async-jest.test.ts +107 -0
- package/src/channel-jest.test.ts +158 -0
- package/src/checker-jest.test.ts +189 -0
- package/src/checker.test.ts +279 -0
- package/src/checker.ts +1861 -0
- package/src/commands/analyze.fl +227 -0
- package/src/commands/auth.fl +315 -0
- package/src/commands/batch.fl +349 -0
- package/src/commands/config.fl +199 -0
- package/src/commands/deploy_key.fl +352 -0
- package/src/commands/issue.fl +275 -0
- package/src/commands/main.fl +492 -0
- package/src/commands/org.fl +425 -0
- package/src/commands/repo.fl +581 -0
- package/src/commands/team.fl +244 -0
- package/src/commands/user.fl +423 -0
- package/src/commands/webhook.fl +400 -0
- package/src/compiler-jest.test.ts +275 -0
- package/src/compiler.test.ts +375 -0
- package/src/compiler.ts +1770 -0
- package/src/config.fl +175 -0
- package/src/core/batch.fl +355 -0
- package/src/core/cache.fl +284 -0
- package/src/core/ensure.fl +324 -0
- package/src/db-100m-full.ts +96 -0
- package/src/db-100m-no-index.ts +133 -0
- package/src/db-100m-real.ts +152 -0
- package/src/db-100m-streaming.ts +154 -0
- package/src/db-100m-test.ts +136 -0
- package/src/db-jest.test.ts +161 -0
- package/src/db-runtime.ts +242 -0
- package/src/db.ts +676 -0
- package/src/errors.fl +134 -0
- package/src/for-of-jest.test.ts +246 -0
- package/src/for-of.test.ts +308 -0
- package/src/function-literal-jest.test.ts +193 -0
- package/src/function-literal.test.ts +248 -0
- package/src/generics-jest.test.ts +104 -0
- package/src/http/client.fl +327 -0
- package/src/ir-gen.ts +459 -0
- package/src/ir.ts +80 -0
- package/src/lexer.test.ts +499 -0
- package/src/lexer.ts +522 -0
- package/src/main.ts +223 -0
- package/src/models.fl +162 -0
- package/src/module-jest.test.ts +145 -0
- package/src/parser.test.ts +542 -0
- package/src/parser.ts +1211 -0
- package/src/pattern-matching-jest.test.ts +170 -0
- package/src/pkg/init.ts +91 -0
- package/src/pkg/install.ts +56 -0
- package/src/pkg/registry.ts +103 -0
- package/src/pkg/run.ts +49 -0
- package/src/pkg/toml.ts +129 -0
- package/src/repl.ts +190 -0
- package/src/runtime/bytecode.ts +291 -0
- package/src/runtime/value.ts +322 -0
- package/src/runtime/vm.ts +354 -0
- package/src/self-host/bootstrap.fl +68 -0
- package/src/self-host/interpreter.fl +361 -0
- package/src/self-host/lexer-simple.fl +22 -0
- package/src/self-host/lexer.fl +305 -0
- package/src/self-host/parser.fl +580 -0
- package/src/struct-instance-jest.test.ts +221 -0
- package/src/struct-instance.test.ts +293 -0
- package/src/struct-jest.test.ts +187 -0
- package/src/struct.test.ts +234 -0
- package/src/trait-jest.test.ts +136 -0
- package/src/vm-jest.test.ts +754 -0
- package/src/vm.ts +1976 -0
- package/src/web-repl/public/index.html +50 -0
- package/src/web-repl/public/main.js +105 -0
- package/src/web-repl/public/style.css +225 -0
- package/src/web-repl/sandbox.ts +88 -0
- package/src/web-repl/server.ts +97 -0
- package/src/while-loop-jest.test.ts +218 -0
- package/src/while-loop.test.ts +267 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Lexer } from "./lexer";
|
|
2
|
+
import { Parser } from "./parser";
|
|
3
|
+
import { Compiler } from "./compiler";
|
|
4
|
+
import { VM } from "./vm";
|
|
5
|
+
|
|
6
|
+
async function exec(source: string): Promise<{ output: string[]; error: string | null }> {
|
|
7
|
+
const { tokens, errors: lexErrors } = new Lexer(source).tokenize();
|
|
8
|
+
if (lexErrors.length > 0) throw new Error(`Lex: ${lexErrors[0].message}`);
|
|
9
|
+
const { program, errors: parseErrors } = new Parser(tokens).parse();
|
|
10
|
+
if (parseErrors.length > 0) throw new Error(`Parse: ${parseErrors[0].message}`);
|
|
11
|
+
const chunk = new Compiler().compile(program);
|
|
12
|
+
return await new VM().run(chunk);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe("Pattern Matching Features - Guard & Destructuring", () => {
|
|
16
|
+
// T1: Guard 절 기본
|
|
17
|
+
it("T1: should support guard clause", async () => {
|
|
18
|
+
const code = `
|
|
19
|
+
fn test() -> i32 {
|
|
20
|
+
var x = 10
|
|
21
|
+
match x {
|
|
22
|
+
y if y > 5 => 100,
|
|
23
|
+
y if y < 5 => 50,
|
|
24
|
+
_ => 0,
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
println(str(test()))
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const { output } = await exec(code);
|
|
31
|
+
expect(output).toEqual(["100"]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// T2: Guard 절에서 패턴 바인딩 사용
|
|
35
|
+
it("T2: should use bindings in guard clause", async () => {
|
|
36
|
+
const code = `
|
|
37
|
+
fn test() -> i32 {
|
|
38
|
+
match 42 {
|
|
39
|
+
x if x > 40 => x + 1,
|
|
40
|
+
_ => 0,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
println(str(test()))
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const { output } = await exec(code);
|
|
47
|
+
expect(output).toEqual(["43"]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// T3: 구조체 분해 패턴 (기본)
|
|
51
|
+
it("T3: should support struct destructuring", async () => {
|
|
52
|
+
const code = `
|
|
53
|
+
struct Point {
|
|
54
|
+
x: i32,
|
|
55
|
+
y: i32,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn test() -> i32 {
|
|
59
|
+
var p = Point { x: 10, y: 20 }
|
|
60
|
+
match p {
|
|
61
|
+
Point { x, y } => x + y,
|
|
62
|
+
_ => 0,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
println(str(test()))
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const { output } = await exec(code);
|
|
69
|
+
expect(output).toEqual(["30"]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// T4: 구조체 분해 + Guard 절
|
|
73
|
+
it("T4: should combine struct destructuring with guard", async () => {
|
|
74
|
+
const code = `
|
|
75
|
+
struct Point {
|
|
76
|
+
x: i32,
|
|
77
|
+
y: i32,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn test() -> i32 {
|
|
81
|
+
var p = Point { x: 10, y: 20 }
|
|
82
|
+
match p {
|
|
83
|
+
Point { x, y } if x > 5 => x + y,
|
|
84
|
+
Point { x, y } if x <= 5 => y - x,
|
|
85
|
+
_ => 0,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
println(str(test()))
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
const { output } = await exec(code);
|
|
92
|
+
expect(output).toEqual(["30"]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// T5: 배열 분해 패턴 (기본)
|
|
96
|
+
it("T5: should support array destructuring", async () => {
|
|
97
|
+
const code = `
|
|
98
|
+
fn test() -> i32 {
|
|
99
|
+
var arr = [10, 20, 30]
|
|
100
|
+
match arr {
|
|
101
|
+
[a, b, c] => a + b + c,
|
|
102
|
+
_ => 0,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
println(str(test()))
|
|
106
|
+
`;
|
|
107
|
+
|
|
108
|
+
const { output } = await exec(code);
|
|
109
|
+
expect(output).toEqual(["60"]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// T6: 배열 분해 + 나머지 패턴
|
|
113
|
+
it("T6: should support rest pattern in array", async () => {
|
|
114
|
+
const code = `
|
|
115
|
+
fn test() -> i32 {
|
|
116
|
+
var arr = [10, 20, 30, 40]
|
|
117
|
+
match arr {
|
|
118
|
+
[a, .., b] => a + b,
|
|
119
|
+
_ => 0,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
println(str(test()))
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
const { output } = await exec(code);
|
|
126
|
+
expect(output).toEqual(["50"]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// T7: 다중 조건 가드
|
|
130
|
+
it("T7: should support multiple guard conditions", async () => {
|
|
131
|
+
const code = `
|
|
132
|
+
fn categorize(x: i32) -> i32 {
|
|
133
|
+
match x {
|
|
134
|
+
y if y < 0 => -1,
|
|
135
|
+
0 => 0,
|
|
136
|
+
y if y > 0 => 1,
|
|
137
|
+
_ => 999,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
println(str(categorize(42)))
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
const { output } = await exec(code);
|
|
144
|
+
expect(output).toEqual(["1"]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// T8: 복잡한 중첩 패턴
|
|
148
|
+
it("T8: should support complex nested patterns", async () => {
|
|
149
|
+
const code = `
|
|
150
|
+
struct Point {
|
|
151
|
+
x: i32,
|
|
152
|
+
y: i32,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
fn test() -> i32 {
|
|
156
|
+
var p = Some(Point { x: 5, y: 10 })
|
|
157
|
+
match p {
|
|
158
|
+
Some(Point { x, y }) if x > 0 => x + y,
|
|
159
|
+
Some(Point { x, y }) if x <= 0 => y - x,
|
|
160
|
+
None => -999,
|
|
161
|
+
_ => 0,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
println(str(test()))
|
|
165
|
+
`;
|
|
166
|
+
|
|
167
|
+
const { output } = await exec(code);
|
|
168
|
+
expect(output).toEqual(["15"]);
|
|
169
|
+
});
|
|
170
|
+
});
|
package/src/pkg/init.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// freelang init [name] - Initialize a new FreeLang project
|
|
2
|
+
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { stringifyTOML } from "./toml";
|
|
6
|
+
|
|
7
|
+
export async function initProject(projectName: string = "my-app"): Promise<void> {
|
|
8
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
9
|
+
|
|
10
|
+
// Check if directory already exists
|
|
11
|
+
if (fs.existsSync(projectPath)) {
|
|
12
|
+
console.error(`Error: Directory '${projectName}' already exists`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Create project structure
|
|
17
|
+
fs.mkdirSync(projectPath, { recursive: true });
|
|
18
|
+
fs.mkdirSync(path.join(projectPath, "src"), { recursive: true });
|
|
19
|
+
fs.mkdirSync(path.join(projectPath, "tests"), { recursive: true });
|
|
20
|
+
|
|
21
|
+
// Create freelang.toml
|
|
22
|
+
const tomlContent = stringifyTOML({
|
|
23
|
+
package: {
|
|
24
|
+
name: projectName,
|
|
25
|
+
version: "0.1.0",
|
|
26
|
+
entry: "src/main.fl",
|
|
27
|
+
},
|
|
28
|
+
dependencies: {},
|
|
29
|
+
scripts: {
|
|
30
|
+
test: "freelang tests/main.fl",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
fs.writeFileSync(path.join(projectPath, "freelang.toml"), tomlContent);
|
|
35
|
+
|
|
36
|
+
// Create main.fl
|
|
37
|
+
const mainContent = `// FreeLang v4 - ${projectName}
|
|
38
|
+
|
|
39
|
+
fn main() -> () {
|
|
40
|
+
println("Hello from FreeLang!")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
main()
|
|
44
|
+
`;
|
|
45
|
+
fs.writeFileSync(path.join(projectPath, "src", "main.fl"), mainContent);
|
|
46
|
+
|
|
47
|
+
// Create test file
|
|
48
|
+
const testContent = `// Tests for ${projectName}
|
|
49
|
+
|
|
50
|
+
fn test_basic() -> bool {
|
|
51
|
+
var x = 42
|
|
52
|
+
x == 42
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Run: freelang tests/main.fl
|
|
56
|
+
`;
|
|
57
|
+
fs.writeFileSync(path.join(projectPath, "tests", "main.fl"), testContent);
|
|
58
|
+
|
|
59
|
+
// Create README.md
|
|
60
|
+
const readmeContent = `# ${projectName}
|
|
61
|
+
|
|
62
|
+
A FreeLang v4 project.
|
|
63
|
+
|
|
64
|
+
## Build
|
|
65
|
+
|
|
66
|
+
\`\`\`bash
|
|
67
|
+
freelang run build
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
## Test
|
|
71
|
+
|
|
72
|
+
\`\`\`bash
|
|
73
|
+
freelang run test
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
## Run
|
|
77
|
+
|
|
78
|
+
\`\`\`bash
|
|
79
|
+
freelang src/main.fl
|
|
80
|
+
\`\`\`
|
|
81
|
+
`;
|
|
82
|
+
fs.writeFileSync(path.join(projectPath, "README.md"), readmeContent);
|
|
83
|
+
|
|
84
|
+
console.log(`✓ Created project '${projectName}' at ${projectPath}`);
|
|
85
|
+
console.log(" - freelang.toml (project configuration)");
|
|
86
|
+
console.log(" - src/main.fl (entry point)");
|
|
87
|
+
console.log(" - tests/main.fl (test file)");
|
|
88
|
+
console.log(" - README.md (documentation)");
|
|
89
|
+
console.log("");
|
|
90
|
+
console.log(`Run 'cd ${projectName} && freelang src/main.fl' to get started!`);
|
|
91
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// freelang install <pkg> - Install a FreeLang package
|
|
2
|
+
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { parseTOML, stringifyTOML } from "./toml";
|
|
6
|
+
|
|
7
|
+
export async function installPackage(
|
|
8
|
+
packageName: string,
|
|
9
|
+
projectRoot: string,
|
|
10
|
+
): Promise<void> {
|
|
11
|
+
const tomlPath = path.join(projectRoot, "freelang.toml");
|
|
12
|
+
|
|
13
|
+
// Read freelang.toml
|
|
14
|
+
if (!fs.existsSync(tomlPath)) {
|
|
15
|
+
console.error("Error: freelang.toml not found");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const tomlContent = fs.readFileSync(tomlPath, "utf-8");
|
|
20
|
+
const tomlData = parseTOML(tomlContent);
|
|
21
|
+
|
|
22
|
+
// Initialize dependencies if not present
|
|
23
|
+
if (!tomlData.dependencies) {
|
|
24
|
+
tomlData.dependencies = {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check if package exists in registry
|
|
28
|
+
const registryPath = path.join(__dirname, "../../packages/registry.json");
|
|
29
|
+
if (!fs.existsSync(registryPath)) {
|
|
30
|
+
console.error("Error: Package registry not found");
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, "utf-8"));
|
|
35
|
+
const pkg = registry.packages[packageName];
|
|
36
|
+
|
|
37
|
+
if (!pkg) {
|
|
38
|
+
console.error(`Error: Package '${packageName}' not found in registry`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Add package to dependencies
|
|
43
|
+
tomlData.dependencies[packageName] = {
|
|
44
|
+
version: pkg.latest_version,
|
|
45
|
+
path: `../../packages/${packageName}/${pkg.latest_version}/src`,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Write updated freelang.toml
|
|
49
|
+
const updatedToml = stringifyTOML(tomlData);
|
|
50
|
+
fs.writeFileSync(tomlPath, updatedToml);
|
|
51
|
+
|
|
52
|
+
console.log(`✓ Installed ${packageName}@${pkg.latest_version}`);
|
|
53
|
+
console.log(
|
|
54
|
+
` Description: ${pkg.description}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Package registry exploration
|
|
2
|
+
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
|
|
6
|
+
export interface Package {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
version: string;
|
|
10
|
+
author?: string;
|
|
11
|
+
repository?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PackageRegistry {
|
|
15
|
+
packages: {
|
|
16
|
+
[name: string]: {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
latest_version: string;
|
|
20
|
+
versions: string[];
|
|
21
|
+
author?: string;
|
|
22
|
+
repository?: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getRegistry(): PackageRegistry {
|
|
28
|
+
const registryPath = path.join(__dirname, "../../packages/registry.json");
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(registryPath)) {
|
|
31
|
+
return { packages: {} };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const content = fs.readFileSync(registryPath, "utf-8");
|
|
36
|
+
return JSON.parse(content);
|
|
37
|
+
} catch {
|
|
38
|
+
return { packages: {} };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function listPackages(): void {
|
|
43
|
+
const registry = getRegistry();
|
|
44
|
+
|
|
45
|
+
if (Object.keys(registry.packages).length === 0) {
|
|
46
|
+
console.log("No packages available in registry");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
console.log("Available FreeLang packages:\n");
|
|
51
|
+
|
|
52
|
+
for (const [name, pkg] of Object.entries(registry.packages)) {
|
|
53
|
+
console.log(`${name}@${pkg.latest_version}`);
|
|
54
|
+
console.log(` ${pkg.description}`);
|
|
55
|
+
if (pkg.author) {
|
|
56
|
+
console.log(` By: ${pkg.author}`);
|
|
57
|
+
}
|
|
58
|
+
console.log("");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function searchPackages(query: string): void {
|
|
63
|
+
const registry = getRegistry();
|
|
64
|
+
const results = Object.entries(registry.packages).filter(([name, pkg]) => {
|
|
65
|
+
return (
|
|
66
|
+
name.includes(query) ||
|
|
67
|
+
pkg.description.toLowerCase().includes(query.toLowerCase())
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (results.length === 0) {
|
|
72
|
+
console.log(`No packages found matching '${query}'`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(`Found ${results.length} package(s) matching '${query}':\n`);
|
|
77
|
+
|
|
78
|
+
for (const [name, pkg] of results) {
|
|
79
|
+
console.log(`${name}@${pkg.latest_version}`);
|
|
80
|
+
console.log(` ${pkg.description}`);
|
|
81
|
+
console.log("");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function getPackageInfo(name: string): void {
|
|
86
|
+
const registry = getRegistry();
|
|
87
|
+
const pkg = registry.packages[name];
|
|
88
|
+
|
|
89
|
+
if (!pkg) {
|
|
90
|
+
console.error(`Package '${name}' not found`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log(`${pkg.name}@${pkg.latest_version}`);
|
|
95
|
+
console.log(`${pkg.description}`);
|
|
96
|
+
if (pkg.author) {
|
|
97
|
+
console.log(`Author: ${pkg.author}`);
|
|
98
|
+
}
|
|
99
|
+
if (pkg.repository) {
|
|
100
|
+
console.log(`Repository: ${pkg.repository}`);
|
|
101
|
+
}
|
|
102
|
+
console.log(`Versions: ${pkg.versions.join(", ")}`);
|
|
103
|
+
}
|
package/src/pkg/run.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// freelang run <script> - Run script from freelang.toml
|
|
2
|
+
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { parseTOML } from "./toml";
|
|
7
|
+
|
|
8
|
+
export async function runScript(
|
|
9
|
+
scriptName: string,
|
|
10
|
+
projectRoot: string,
|
|
11
|
+
): Promise<void> {
|
|
12
|
+
const tomlPath = path.join(projectRoot, "freelang.toml");
|
|
13
|
+
|
|
14
|
+
// Read freelang.toml
|
|
15
|
+
if (!fs.existsSync(tomlPath)) {
|
|
16
|
+
console.error("Error: freelang.toml not found");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const tomlContent = fs.readFileSync(tomlPath, "utf-8");
|
|
21
|
+
const tomlData = parseTOML(tomlContent);
|
|
22
|
+
|
|
23
|
+
// Get script
|
|
24
|
+
if (!tomlData.scripts || !tomlData.scripts[scriptName]) {
|
|
25
|
+
console.error(`Error: Script '${scriptName}' not found in freelang.toml`);
|
|
26
|
+
console.log("Available scripts:");
|
|
27
|
+
if (tomlData.scripts) {
|
|
28
|
+
for (const [name] of Object.entries(tomlData.scripts)) {
|
|
29
|
+
console.log(` - ${name}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const scriptCommand = tomlData.scripts[scriptName];
|
|
36
|
+
|
|
37
|
+
console.log(`> ${scriptCommand}`);
|
|
38
|
+
console.log("");
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
execSync(scriptCommand, {
|
|
42
|
+
cwd: projectRoot,
|
|
43
|
+
stdio: "inherit",
|
|
44
|
+
});
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error("Script failed");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/pkg/toml.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Lightweight TOML Parser (no external dependencies)
|
|
2
|
+
|
|
3
|
+
export interface TomlValue {
|
|
4
|
+
[key: string]: any;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function parseTOML(content: string): TomlValue {
|
|
8
|
+
const result: TomlValue = {};
|
|
9
|
+
const lines = content.split("\n");
|
|
10
|
+
let currentSection: string | null = null;
|
|
11
|
+
let currentObj: TomlValue = result;
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < lines.length; i++) {
|
|
14
|
+
const line = lines[i].trim();
|
|
15
|
+
|
|
16
|
+
// Skip empty lines and comments
|
|
17
|
+
if (!line || line.startsWith("#")) continue;
|
|
18
|
+
|
|
19
|
+
// Section header: [section]
|
|
20
|
+
if (line.startsWith("[") && line.endsWith("]")) {
|
|
21
|
+
currentSection = line.slice(1, -1).trim();
|
|
22
|
+
if (!result[currentSection]) {
|
|
23
|
+
result[currentSection] = {};
|
|
24
|
+
}
|
|
25
|
+
currentObj = result[currentSection];
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Key-value pairs: key = value
|
|
30
|
+
const eqIndex = line.indexOf("=");
|
|
31
|
+
if (eqIndex > 0) {
|
|
32
|
+
const key = line.substring(0, eqIndex).trim();
|
|
33
|
+
const valueStr = line.substring(eqIndex + 1).trim();
|
|
34
|
+
const value = parseValue(valueStr);
|
|
35
|
+
currentObj[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseValue(valueStr: string): any {
|
|
43
|
+
// String values (double quotes)
|
|
44
|
+
if (valueStr.startsWith('"') && valueStr.endsWith('"')) {
|
|
45
|
+
return valueStr.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// String values (single quotes)
|
|
49
|
+
if (valueStr.startsWith("'") && valueStr.endsWith("'")) {
|
|
50
|
+
return valueStr.slice(1, -1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Boolean values
|
|
54
|
+
if (valueStr === "true") return true;
|
|
55
|
+
if (valueStr === "false") return false;
|
|
56
|
+
|
|
57
|
+
// Numeric values
|
|
58
|
+
if (!isNaN(Number(valueStr))) {
|
|
59
|
+
return Number(valueStr);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Array values [item1, item2, ...]
|
|
63
|
+
if (valueStr.startsWith("[") && valueStr.endsWith("]")) {
|
|
64
|
+
const itemsStr = valueStr.slice(1, -1);
|
|
65
|
+
const items = itemsStr.split(",").map((item) => parseValue(item.trim()));
|
|
66
|
+
return items;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Table values {key = value, ...}
|
|
70
|
+
if (valueStr.startsWith("{") && valueStr.endsWith("}")) {
|
|
71
|
+
const content = valueStr.slice(1, -1);
|
|
72
|
+
const obj: TomlValue = {};
|
|
73
|
+
const pairs = content.split(",");
|
|
74
|
+
for (const pair of pairs) {
|
|
75
|
+
const eqIdx = pair.indexOf("=");
|
|
76
|
+
if (eqIdx > 0) {
|
|
77
|
+
const k = pair.substring(0, eqIdx).trim();
|
|
78
|
+
const v = pair.substring(eqIdx + 1).trim();
|
|
79
|
+
obj[k] = parseValue(v);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return obj;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Return as string if no match
|
|
86
|
+
return valueStr;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function stringifyTOML(obj: TomlValue, indent: number = 0): string {
|
|
90
|
+
const lines: string[] = [];
|
|
91
|
+
const indentStr = " ".repeat(indent);
|
|
92
|
+
|
|
93
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
94
|
+
if (value === null || value === undefined) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
99
|
+
// Nested section
|
|
100
|
+
if (indent === 0) {
|
|
101
|
+
lines.push(`[${key}]`);
|
|
102
|
+
}
|
|
103
|
+
lines.push(stringifyTOML(value, indent + 2));
|
|
104
|
+
} else if (Array.isArray(value)) {
|
|
105
|
+
// Array
|
|
106
|
+
const items = value.map((v) => stringifyValue(v)).join(", ");
|
|
107
|
+
lines.push(`${indentStr}${key} = [${items}]`);
|
|
108
|
+
} else {
|
|
109
|
+
// Scalar value
|
|
110
|
+
lines.push(`${indentStr}${key} = ${stringifyValue(value)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return lines.filter((l) => l).join("\n");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function stringifyValue(value: any): string {
|
|
118
|
+
if (typeof value === "string") {
|
|
119
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
120
|
+
}
|
|
121
|
+
if (typeof value === "boolean") {
|
|
122
|
+
return value ? "true" : "false";
|
|
123
|
+
}
|
|
124
|
+
if (Array.isArray(value)) {
|
|
125
|
+
const items = value.map((v) => stringifyValue(v)).join(", ");
|
|
126
|
+
return `[${items}]`;
|
|
127
|
+
}
|
|
128
|
+
return String(value);
|
|
129
|
+
}
|