oclang 0.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/.editorconfig +12 -0
- package/CHANGELOG.md +27 -0
- package/README.md +15 -0
- package/package.json +19 -0
- package/src/core/ast/astBuilder.ts +172 -0
- package/src/core/ast/types/base/index.ts +2 -0
- package/src/core/ast/types/base/source.ts +12 -0
- package/src/core/ast/types/base/statement.ts +13 -0
- package/src/core/ast/types/statements/callStatement.ts +6 -0
- package/src/core/ast/types/statements/functionStatement.ts +16 -0
- package/src/core/ast/types/statements/index.ts +4 -0
- package/src/core/ast/types/statements/printStatement.ts +8 -0
- package/src/core/ast/types/statements/variableStatement.ts +9 -0
- package/src/core/context/contextType.ts +4 -0
- package/src/core/context/coreContext.ts +14 -0
- package/src/core/index.ts +17 -0
- package/src/core/lexer/tokens/attrs.ts +8 -0
- package/src/core/lexer/tokens/comments.ts +14 -0
- package/src/core/lexer/tokens/delimiters.ts +15 -0
- package/src/core/lexer/tokens/function.ts +13 -0
- package/src/core/lexer/tokens/ignored.ts +5 -0
- package/src/core/lexer/tokens/index.ts +31 -0
- package/src/core/lexer/tokens/keywords.ts +5 -0
- package/src/core/lexer/tokens/literals.ts +16 -0
- package/src/core/lexer/tokens/operators.ts +5 -0
- package/src/core/lexer/tokens/vars.ts +14 -0
- package/src/core/lexer/tokens.ts +4 -0
- package/src/core/parser/parser.ts +97 -0
- package/src/core/runner/runner.ts +128 -0
- package/src/core/runner/utils/string.ts +19 -0
- package/src/index.ts +8 -0
- package/src/shared/manager/baseManager.ts +24 -0
- package/src/shared/manager/errors/api/index.ts +2 -0
- package/src/shared/manager/errors/api/throw.ts +5 -0
- package/src/shared/manager/errors/api/types.ts +5 -0
- package/src/shared/manager/errors/coreErrors.ts +10 -0
- package/src/shared/manager/errors/semantic/undefined/declared.ts +28 -0
- package/src/shared/manager/errors/semantic/undefined/undeclared.ts +21 -0
- package/src/shared/manager/errors/syntax/syntaxErrors.ts +8 -0
- package/src/shared/manager/errors/syntax/token.ts +13 -0
- package/src/shared/manager/warn/coreWarnings.ts +8 -0
- package/src/shared/models/func.ts +5 -0
- package/src/shared/models/value.ts +11 -0
- package/src/shared/models/var.ts +7 -0
- package/tsconfig.json +27 -0
package/.editorconfig
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## System version
|
|
4
|
+
|
|
5
|
+
MAJOR.MINOR.PATCH
|
|
6
|
+
|
|
7
|
+
MAJOR version when you make incompatible and breaking changes,
|
|
8
|
+
MINOR version when you add functionality in a backwards-compatible manner, and
|
|
9
|
+
PATCH version when you make backwards-compatible bug fixes.
|
|
10
|
+
|
|
11
|
+
## 0.1.0 - Output
|
|
12
|
+
|
|
13
|
+
- Initial release
|
|
14
|
+
- Basic output `print("Hello World")`
|
|
15
|
+
- Basic variable declaration `number x = 10`
|
|
16
|
+
- Basic printing of variables `print(x)`
|
|
17
|
+
|
|
18
|
+
## 0.2.0 - Variables
|
|
19
|
+
|
|
20
|
+
- Basic variable modification `set number x = 20`
|
|
21
|
+
|
|
22
|
+
## 0.3.0 - Generics
|
|
23
|
+
|
|
24
|
+
- Error and warning handling system
|
|
25
|
+
- Functions
|
|
26
|
+
- Constants and set. Constants can't be modified
|
|
27
|
+
-> AST added
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# ocat_lang
|
|
2
|
+
|
|
3
|
+
To install dependencies:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun install
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
To run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun run src/index.ts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This project was created using `bun init` in bun v1.1.33. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "oclang",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "A programming language",
|
|
5
|
+
"author": "LuisRG-L, Orange Cat",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"module": "src/index.ts",
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@types/bun": "latest"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"typescript": "^5.0.0"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"chalk": "^5.6.2",
|
|
17
|
+
"chevrotain": "^11.0.3"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AlreadyDeclaredFunctionError,
|
|
3
|
+
AlreadyDeclaredVariableError,
|
|
4
|
+
CantModifyConstError,
|
|
5
|
+
} from "../../shared/manager/errors/semantic/undefined/declared.js";
|
|
6
|
+
import {
|
|
7
|
+
UndeclaredFunctionError,
|
|
8
|
+
UndeclaredVariableError,
|
|
9
|
+
} from "../../shared/manager/errors/semantic/undefined/undeclared.js";
|
|
10
|
+
import { type CoreContext } from "../context/coreContext.js";
|
|
11
|
+
import { ValueType } from "../../shared/models/value.js";
|
|
12
|
+
import { StatementKind } from "./types/base/index.js";
|
|
13
|
+
import type { AnyStatement, PrintStatement } from "./types/statements/index.js";
|
|
14
|
+
import { isNoSubstitutionTemplateLiteral } from "typescript";
|
|
15
|
+
|
|
16
|
+
export type BuildType = AnyStatement | null;
|
|
17
|
+
|
|
18
|
+
export function buildAst(cst: any): AnyStatement[] {
|
|
19
|
+
const statements = cst.children.statement ?? [];
|
|
20
|
+
const ast: AnyStatement[] = [];
|
|
21
|
+
|
|
22
|
+
for (const statement of statements) {
|
|
23
|
+
let __tmp: BuildType = null;
|
|
24
|
+
const __$tmp = () => { if (__tmp) ast.push(__tmp); }
|
|
25
|
+
__tmp = printStatement(statement);
|
|
26
|
+
__$tmp();
|
|
27
|
+
__tmp = variableStatement(statement);
|
|
28
|
+
__$tmp();
|
|
29
|
+
__tmp = functionStatement(statement);
|
|
30
|
+
__$tmp();
|
|
31
|
+
__tmp = callStatement(statement);
|
|
32
|
+
__$tmp();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return ast;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type FBuilder = (statement: any) => AnyStatement | null;
|
|
39
|
+
|
|
40
|
+
const printStatement: FBuilder = (statement: any) => {
|
|
41
|
+
const printStmt = statement.children.printStatement?.[0];
|
|
42
|
+
if (printStmt) {
|
|
43
|
+
const strToken = printStmt.children.StringLiteral?.[0];
|
|
44
|
+
if (strToken) {
|
|
45
|
+
return {
|
|
46
|
+
kind: StatementKind.PrintStatement,
|
|
47
|
+
sourceInfo: {
|
|
48
|
+
tokens: printStmt.children,
|
|
49
|
+
cstNode: printStmt,
|
|
50
|
+
startLine: printStmt.startLine,
|
|
51
|
+
endLine: printStmt.endLine,
|
|
52
|
+
startColumn: printStmt.startColumn,
|
|
53
|
+
endColumn: printStmt.endColumn,
|
|
54
|
+
},
|
|
55
|
+
value: {
|
|
56
|
+
value: strToken.image,
|
|
57
|
+
type: ValueType.String,
|
|
58
|
+
}
|
|
59
|
+
} as PrintStatement;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const idToken = printStmt.children.Identifier?.[0];
|
|
63
|
+
if (idToken) {
|
|
64
|
+
return {
|
|
65
|
+
kind: StatementKind.PrintStatement,
|
|
66
|
+
sourceInfo: {
|
|
67
|
+
tokens: printStmt.children,
|
|
68
|
+
cstNode: printStmt,
|
|
69
|
+
startLine: printStmt.startLine,
|
|
70
|
+
endLine: printStmt.endLine,
|
|
71
|
+
startColumn: printStmt.startColumn,
|
|
72
|
+
endColumn: printStmt.endColumn,
|
|
73
|
+
},
|
|
74
|
+
value: {
|
|
75
|
+
value: idToken.image,
|
|
76
|
+
type: ValueType.Identifier,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const variableStatement: FBuilder = (statement: any) => {
|
|
85
|
+
const varStmt = statement.children.variableStatement?.[0];
|
|
86
|
+
if (varStmt) {
|
|
87
|
+
const typeToken = varStmt.children.VarType?.[0];
|
|
88
|
+
const idToken = varStmt.children.Identifier?.[0];
|
|
89
|
+
const valueToken =
|
|
90
|
+
varStmt.children.StringLiteral?.[0] ??
|
|
91
|
+
varStmt.children.NumberLiteral?.[0] ??
|
|
92
|
+
varStmt.children.BooleanLiteral?.[0];
|
|
93
|
+
|
|
94
|
+
if (!typeToken || !idToken || !valueToken) return null;
|
|
95
|
+
|
|
96
|
+
const setToken = varStmt.children.Set?.[0];
|
|
97
|
+
const constToken = varStmt.children.Const?.[0];
|
|
98
|
+
|
|
99
|
+
const type = typeToken.image as ValueType;
|
|
100
|
+
const id = idToken.image;
|
|
101
|
+
const value: string = valueToken.image;
|
|
102
|
+
return {
|
|
103
|
+
kind: StatementKind.VariableStatement,
|
|
104
|
+
sourceInfo: {
|
|
105
|
+
tokens: varStmt.children,
|
|
106
|
+
cstNode: varStmt,
|
|
107
|
+
startLine: varStmt.startLine,
|
|
108
|
+
endLine: varStmt.endLine,
|
|
109
|
+
startColumn: varStmt.startColumn,
|
|
110
|
+
endColumn: varStmt.endColumn,
|
|
111
|
+
},
|
|
112
|
+
id,
|
|
113
|
+
var: {
|
|
114
|
+
type,
|
|
115
|
+
value,
|
|
116
|
+
props: {
|
|
117
|
+
isConst: !!constToken,
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
set: !!setToken,
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const functionStatement: FBuilder = (statement: any) => {
|
|
127
|
+
const funcStmt = statement.children.functionStatement?.[0];
|
|
128
|
+
if (funcStmt) {
|
|
129
|
+
const idToken = funcStmt.children.Identifier?.[0];
|
|
130
|
+
if (!idToken) return null;
|
|
131
|
+
const id = idToken.image;
|
|
132
|
+
|
|
133
|
+
const body = funcStmt.children.statement ?? [];
|
|
134
|
+
return {
|
|
135
|
+
kind: StatementKind.FunctionStatement,
|
|
136
|
+
sourceInfo: {
|
|
137
|
+
tokens: funcStmt.children,
|
|
138
|
+
cstNode: funcStmt,
|
|
139
|
+
startLine: funcStmt.startLine,
|
|
140
|
+
endLine: funcStmt.endLine,
|
|
141
|
+
startColumn: funcStmt.startColumn,
|
|
142
|
+
endColumn: funcStmt.endColumn,
|
|
143
|
+
},
|
|
144
|
+
id,
|
|
145
|
+
body,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const callStatement: FBuilder = (statement: any) => {
|
|
152
|
+
const callStmt = statement.children.callStatement?.[0];
|
|
153
|
+
if (callStmt) {
|
|
154
|
+
const idToken = callStmt.children.Identifier?.[0];
|
|
155
|
+
if (!idToken) return null;
|
|
156
|
+
const id = idToken.image;
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
kind: StatementKind.CallStatement,
|
|
160
|
+
sourceInfo: {
|
|
161
|
+
tokens: callStmt.children,
|
|
162
|
+
cstNode: callStmt,
|
|
163
|
+
startLine: callStmt.startLine,
|
|
164
|
+
endLine: callStmt.endLine,
|
|
165
|
+
startColumn: callStmt.startColumn,
|
|
166
|
+
endColumn: callStmt.endColumn,
|
|
167
|
+
},
|
|
168
|
+
id
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null
|
|
172
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Statement, StatementKind } from "../base";
|
|
2
|
+
import type { CallStatement } from "./callStatement";
|
|
3
|
+
import type { PrintStatement } from "./printStatement";
|
|
4
|
+
import type { VariableStatement } from "./variableStatement";
|
|
5
|
+
|
|
6
|
+
export type AnyStatement =
|
|
7
|
+
| PrintStatement
|
|
8
|
+
| VariableStatement
|
|
9
|
+
| FunctionStatement
|
|
10
|
+
| CallStatement;
|
|
11
|
+
|
|
12
|
+
export interface FunctionStatement extends Statement {
|
|
13
|
+
kind: StatementKind.FunctionStatement;
|
|
14
|
+
id: string;
|
|
15
|
+
body: AnyStatement[];
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Variable } from "../../../../shared/models/var";
|
|
2
|
+
import type { Statement, StatementKind } from "../base";
|
|
3
|
+
|
|
4
|
+
export interface VariableStatement extends Statement {
|
|
5
|
+
kind: StatementKind.VariableStatement;
|
|
6
|
+
id: string;
|
|
7
|
+
var: Variable;
|
|
8
|
+
set: boolean;
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Variable } from "../../shared/models/var";
|
|
2
|
+
import type { Function } from "../../shared/models/func";
|
|
3
|
+
|
|
4
|
+
export interface CoreContext {
|
|
5
|
+
variables: Record<string, Variable>;
|
|
6
|
+
functions: Record<string, Function>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createCoreContext(): CoreContext {
|
|
10
|
+
return {
|
|
11
|
+
variables: {},
|
|
12
|
+
functions: {},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ocatLexer } from "./lexer/tokens";
|
|
2
|
+
import { OcatParser } from "./parser/parser";
|
|
3
|
+
import { run } from "./runner/runner";
|
|
4
|
+
import { createCoreContext } from "./context/coreContext";
|
|
5
|
+
import { buildAst } from "./ast/astBuilder";
|
|
6
|
+
|
|
7
|
+
export function execute(code: string) {
|
|
8
|
+
const lexingResult = ocatLexer.tokenize(code);
|
|
9
|
+
|
|
10
|
+
const parser = new OcatParser();
|
|
11
|
+
parser.input = lexingResult.tokens;
|
|
12
|
+
const cst = parser.program();
|
|
13
|
+
const ast = buildAst(cst, );
|
|
14
|
+
|
|
15
|
+
const context = createCoreContext();
|
|
16
|
+
run(ast, context);
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createToken, Lexer } from "chevrotain";
|
|
2
|
+
|
|
3
|
+
export const LineComment = createToken({
|
|
4
|
+
name: "LineComment",
|
|
5
|
+
pattern: /\/\/[^\n]*/,
|
|
6
|
+
group: Lexer.SKIPPED,
|
|
7
|
+
});
|
|
8
|
+
export const BlockComment = createToken({
|
|
9
|
+
name: "BlockComment",
|
|
10
|
+
pattern: /\/\*[\s\S]*?\*\//,
|
|
11
|
+
group: Lexer.SKIPPED,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const comments = [LineComment, BlockComment];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createToken } from "chevrotain";
|
|
2
|
+
|
|
3
|
+
export const LeftParen = createToken({ name: "LeftParen", pattern: /\(/ });
|
|
4
|
+
export const RightParen = createToken({ name: "RightParen", pattern: /\)/ });
|
|
5
|
+
|
|
6
|
+
export const LeftBrace = createToken({ name: "LeftBrace", pattern: /\{/ });
|
|
7
|
+
export const RightBrace = createToken({ name: "RightBrace", pattern: /\}/ });
|
|
8
|
+
|
|
9
|
+
export const LeftBracket = createToken({ name: "LeftBracket", pattern: /\[/ });
|
|
10
|
+
export const RightBracket = createToken({ name: "RightBracket", pattern: /\]/ });
|
|
11
|
+
|
|
12
|
+
export const LeftTag = createToken({ name: "LeftTag", pattern: /</ });
|
|
13
|
+
export const RightTag = createToken({ name: "RightTag", pattern: />/ });
|
|
14
|
+
|
|
15
|
+
export const delimiters = [LeftParen, RightParen, LeftBrace, RightBrace, LeftBracket, RightBracket, LeftTag, RightTag];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createToken } from "chevrotain";
|
|
2
|
+
|
|
3
|
+
export const Function = createToken({
|
|
4
|
+
name: "Function",
|
|
5
|
+
pattern: /func/,
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const Call = createToken({
|
|
9
|
+
name: "Call",
|
|
10
|
+
pattern: /call/,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const functions = [Function, Call];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export * from "./ignored.js";
|
|
2
|
+
export * from "./comments.js";
|
|
3
|
+
export * from "./keywords.js";
|
|
4
|
+
export * from "./literals.js";
|
|
5
|
+
export * from "./delimiters.js";
|
|
6
|
+
export * from "./vars.js";
|
|
7
|
+
export * from "./operators.js";
|
|
8
|
+
export * from "./attrs.js";
|
|
9
|
+
export * from "./function.js";
|
|
10
|
+
|
|
11
|
+
import { ignored } from "./ignored.js";
|
|
12
|
+
import { comments } from "./comments.js";
|
|
13
|
+
import { keywords } from "./keywords.js";
|
|
14
|
+
import { literals } from "./literals.js";
|
|
15
|
+
import { delimiters } from "./delimiters.js";
|
|
16
|
+
import { variablesAndConstants } from "./vars.js";
|
|
17
|
+
import { operators } from "./operators.js";
|
|
18
|
+
import { attributes } from "./attrs.js";
|
|
19
|
+
import { functions } from "./function.js";
|
|
20
|
+
|
|
21
|
+
export const allTokens = [
|
|
22
|
+
...ignored,
|
|
23
|
+
...comments,
|
|
24
|
+
...keywords,
|
|
25
|
+
...attributes,
|
|
26
|
+
...functions,
|
|
27
|
+
...literals,
|
|
28
|
+
...delimiters,
|
|
29
|
+
...variablesAndConstants,
|
|
30
|
+
...operators,
|
|
31
|
+
];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createToken } from "chevrotain";
|
|
2
|
+
|
|
3
|
+
export const StringLiteral = createToken({
|
|
4
|
+
name: "StringLiteral",
|
|
5
|
+
pattern: /"(?:[^\\"]|\\.)*"/,
|
|
6
|
+
});
|
|
7
|
+
export const NumberLiteral = createToken({
|
|
8
|
+
name: "NumberLiteral",
|
|
9
|
+
pattern: /\d+(\.\d+)?/,
|
|
10
|
+
});
|
|
11
|
+
export const BooleanLiteral = createToken({
|
|
12
|
+
name: "BooleanLiteral",
|
|
13
|
+
pattern: /true|false/,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const literals = [StringLiteral, NumberLiteral, BooleanLiteral];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createToken } from "chevrotain";
|
|
2
|
+
import { ValueType } from "../../../shared/models/value.js";
|
|
3
|
+
|
|
4
|
+
export const VarType = createToken({
|
|
5
|
+
name: "VarType",
|
|
6
|
+
pattern: new RegExp(Object.values(ValueType).join("|")),
|
|
7
|
+
});
|
|
8
|
+
export const Set = createToken({ name: "Set", pattern: /set/ });
|
|
9
|
+
export const Identifier = createToken({
|
|
10
|
+
name: "Identifier",
|
|
11
|
+
pattern: /[a-zA-Z_]\w*/,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const variablesAndConstants = [VarType, Set, Identifier];
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { CstParser } from "chevrotain";
|
|
2
|
+
import { ValueType } from "../../shared/models/value.js";
|
|
3
|
+
import * as token from "../lexer/tokens/index.js";
|
|
4
|
+
|
|
5
|
+
export class OcatParser extends CstParser {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(token.allTokens, {
|
|
8
|
+
recoveryEnabled: true,
|
|
9
|
+
});
|
|
10
|
+
this.performSelfAnalysis();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public program = this.RULE("program", () => {
|
|
14
|
+
this.MANY(() => this.SUBRULE(this.statement));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
public statement = this.RULE("statement", () => {
|
|
18
|
+
this.OR([
|
|
19
|
+
{
|
|
20
|
+
ALT: () =>
|
|
21
|
+
this.SUBRULE(this.printStatement, {
|
|
22
|
+
LABEL: "printStatement",
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
ALT: () =>
|
|
27
|
+
this.SUBRULE(this.variableStatement, {
|
|
28
|
+
LABEL: "variableStatement",
|
|
29
|
+
}),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
ALT: () =>
|
|
33
|
+
this.SUBRULE(this.functionStatement, {
|
|
34
|
+
LABEL: "functionStatement",
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
ALT: () =>
|
|
39
|
+
this.SUBRULE(this.callStatement, {
|
|
40
|
+
LABEL: "callStatement",
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
public printStatement = this.RULE("printStatement", () => {
|
|
47
|
+
this.CONSUME(token.Output);
|
|
48
|
+
this.CONSUME(token.LeftParen);
|
|
49
|
+
this.OR([
|
|
50
|
+
...token.literals.map((lit) => ({
|
|
51
|
+
ALT: () => this.CONSUME(lit, { LABEL: lit.name }),
|
|
52
|
+
})),
|
|
53
|
+
{
|
|
54
|
+
ALT: () =>
|
|
55
|
+
this.CONSUME(token.Identifier, { LABEL: "Identifier" }),
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
58
|
+
this.CONSUME(token.RightParen);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
public variableStatement = this.RULE("variableStatement", () => {
|
|
62
|
+
this.OPTION(() => {
|
|
63
|
+
this.OR([
|
|
64
|
+
{ ALT: () => this.CONSUME(token.Set, { LABEL: "Set" }) },
|
|
65
|
+
{ ALT: () => this.CONSUME(token.Const, { LABEL: "Const" }) },
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
const type = this.CONSUME(token.VarType).image as ValueType;
|
|
69
|
+
this.CONSUME(token.Identifier);
|
|
70
|
+
this.CONSUME(token.Assign);
|
|
71
|
+
|
|
72
|
+
switch (type) {
|
|
73
|
+
case ValueType.String:
|
|
74
|
+
this.CONSUME(token.StringLiteral);
|
|
75
|
+
break;
|
|
76
|
+
case ValueType.Number:
|
|
77
|
+
this.CONSUME(token.NumberLiteral);
|
|
78
|
+
break;
|
|
79
|
+
case ValueType.Boolean:
|
|
80
|
+
this.CONSUME(token.BooleanLiteral);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
public functionStatement = this.RULE("functionStatement", () => {
|
|
86
|
+
this.CONSUME(token.Function);
|
|
87
|
+
this.CONSUME(token.Identifier);
|
|
88
|
+
this.CONSUME(token.LeftBrace);
|
|
89
|
+
this.MANY(() => this.SUBRULE(this.statement));
|
|
90
|
+
this.CONSUME(token.RightBrace);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
public callStatement = this.RULE("callStatement", () => {
|
|
94
|
+
this.CONSUME(token.Call);
|
|
95
|
+
this.CONSUME(token.Identifier);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AlreadyDeclaredFunctionError,
|
|
3
|
+
AlreadyDeclaredVariableError,
|
|
4
|
+
CantModifyConstError,
|
|
5
|
+
} from "../../shared/manager/errors/semantic/undefined/declared.js";
|
|
6
|
+
import {
|
|
7
|
+
UndeclaredFunctionError,
|
|
8
|
+
UndeclaredVariableError,
|
|
9
|
+
} from "../../shared/manager/errors/semantic/undefined/undeclared.js";
|
|
10
|
+
import { type CoreContext } from "../context/coreContext.js";
|
|
11
|
+
import { ValueType } from "../../shared/models/value.js";
|
|
12
|
+
import { solveString } from "./utils/string.js";
|
|
13
|
+
import type { AnyStatement, CallStatement, FunctionStatement, PrintStatement, VariableStatement } from "../ast/types/statements/index.js";
|
|
14
|
+
import { StatementKind } from "../ast/types/base/statement.js";
|
|
15
|
+
|
|
16
|
+
export function run(ast: AnyStatement[], context: CoreContext) {
|
|
17
|
+
|
|
18
|
+
for (const statement of ast) {
|
|
19
|
+
switch (statement.kind) {
|
|
20
|
+
case StatementKind.PrintStatement:
|
|
21
|
+
printStatement(statement, context);
|
|
22
|
+
break;
|
|
23
|
+
case StatementKind.VariableStatement:
|
|
24
|
+
variableStatement(statement, context);
|
|
25
|
+
break;
|
|
26
|
+
case StatementKind.FunctionStatement:
|
|
27
|
+
functionStatement(statement, context);
|
|
28
|
+
break;
|
|
29
|
+
case StatementKind.CallStatement:
|
|
30
|
+
callStatement(statement, context);
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function printStatement(statement: PrintStatement, context: CoreContext) {
|
|
37
|
+
let val;
|
|
38
|
+
switch (statement.value.type) {
|
|
39
|
+
case ValueType.Identifier:
|
|
40
|
+
const id = statement.value.value;
|
|
41
|
+
const varData = context.variables[id];
|
|
42
|
+
if (varData !== undefined) {
|
|
43
|
+
val = varData.value;
|
|
44
|
+
} else {
|
|
45
|
+
new UndeclaredVariableError(id).throw(statement.sourceInfo.startLine);
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case ValueType.String:
|
|
50
|
+
val = solveString(statement.value.value, context, (err) => {
|
|
51
|
+
err.throw(statement.sourceInfo.startLine);
|
|
52
|
+
})
|
|
53
|
+
break;
|
|
54
|
+
|
|
55
|
+
default:
|
|
56
|
+
val = statement.value.value;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
console.log(val);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function variableStatement(statement: VariableStatement, context: CoreContext) {
|
|
63
|
+
const varId = statement.id;
|
|
64
|
+
if (statement.set) {
|
|
65
|
+
if (!context.variables[varId]) {
|
|
66
|
+
new UndeclaredVariableError(varId).throw(
|
|
67
|
+
statement.sourceInfo.startLine
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (context.variables[varId]?.props.isConst) {
|
|
71
|
+
new CantModifyConstError(varId).throw(
|
|
72
|
+
statement.sourceInfo.startLine
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
} else if (!statement.var.props.isConst) {
|
|
76
|
+
if (context.variables[varId]) {
|
|
77
|
+
new AlreadyDeclaredVariableError(varId).throw(
|
|
78
|
+
statement.sourceInfo.startLine
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let value: string = statement.var.value;
|
|
84
|
+
|
|
85
|
+
switch (statement.var.type) {
|
|
86
|
+
case ValueType.String:
|
|
87
|
+
value = solveString(value, context, (err) => {
|
|
88
|
+
err.throw(statement.sourceInfo.startLine);
|
|
89
|
+
});
|
|
90
|
+
break;
|
|
91
|
+
default:
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
context.variables[varId] = {
|
|
96
|
+
type: statement.var.type,
|
|
97
|
+
value,
|
|
98
|
+
props: {
|
|
99
|
+
isConst: !!statement.var.props.isConst,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function functionStatement(statement: FunctionStatement, context: CoreContext) {
|
|
105
|
+
const idToken = statement.id;
|
|
106
|
+
if (!idToken) return;
|
|
107
|
+
|
|
108
|
+
const funcData = context.functions[idToken];
|
|
109
|
+
if (funcData) {
|
|
110
|
+
new AlreadyDeclaredFunctionError(idToken).throw(statement.sourceInfo.startLine);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const body = statement.body ?? [];
|
|
114
|
+
context.functions[idToken] = {
|
|
115
|
+
body,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function callStatement(statement: CallStatement, context: CoreContext) {
|
|
120
|
+
const funcId = statement.id
|
|
121
|
+
if (!funcId) return;
|
|
122
|
+
|
|
123
|
+
const funcData = context.functions[funcId];
|
|
124
|
+
if (!funcData) {
|
|
125
|
+
new UndeclaredFunctionError(funcId).throw(statement.sourceInfo.startLine);
|
|
126
|
+
}
|
|
127
|
+
run(funcData.body, context);
|
|
128
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OcatError } from "../../../shared/manager/errors/coreErrors";
|
|
2
|
+
import { UndeclaredVariableError } from "../../../shared/manager/errors/semantic/undefined/undeclared";
|
|
3
|
+
import type { CoreContext } from "../../context/coreContext";
|
|
4
|
+
|
|
5
|
+
export function solveString(input: string, context: CoreContext, err: (err: OcatError) => void): string {
|
|
6
|
+
let str: string = input;
|
|
7
|
+
|
|
8
|
+
str = str.slice(1, -1);
|
|
9
|
+
|
|
10
|
+
str = str.replace(/\$\{([^}]+)\}/g, (match, varName) => {
|
|
11
|
+
const value = context.variables[varName.trim()];
|
|
12
|
+
if (!value) {
|
|
13
|
+
err(new UndeclaredVariableError(varName.trim()));
|
|
14
|
+
}
|
|
15
|
+
return value?.value ?? "";
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return str;
|
|
19
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import chalk, { type ChalkInstance } from "chalk";
|
|
2
|
+
|
|
3
|
+
export class OcatManager {
|
|
4
|
+
public name: string;
|
|
5
|
+
public message: string;
|
|
6
|
+
public color: ChalkInstance;
|
|
7
|
+
|
|
8
|
+
constructor(name: string, color: ChalkInstance) {
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.message = "";
|
|
11
|
+
this.color = color;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
toString(line: number | undefined = undefined): string {
|
|
15
|
+
return `${this.color.bold(this.name)} ${
|
|
16
|
+
line && chalk.gray(`at line ${line}`)
|
|
17
|
+
}: ${this.color.italic(this.message)}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
throw(line: number) {
|
|
21
|
+
console.log(this.toString(line));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ContextType } from "../../../../../core/context/contextType";
|
|
2
|
+
import { OcatError } from "../../coreErrors";
|
|
3
|
+
|
|
4
|
+
export class AlreadyDeclaredError extends OcatError {
|
|
5
|
+
constructor(varName: string, type: ContextType) {
|
|
6
|
+
super(`Already declared ${type.toLowerCase()}`);
|
|
7
|
+
this.message = `${type} ${varName} is already declared`;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class AlreadyDeclaredVariableError extends AlreadyDeclaredError {
|
|
12
|
+
constructor(varName: string) {
|
|
13
|
+
super(varName, ContextType.Variable);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class AlreadyDeclaredFunctionError extends AlreadyDeclaredError {
|
|
18
|
+
constructor(varName: string) {
|
|
19
|
+
super(varName, ContextType.Function);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class CantModifyConstError extends OcatError {
|
|
24
|
+
constructor(varName: string) {
|
|
25
|
+
super(`Can't modify const variable`);
|
|
26
|
+
this.message = `Variable ${varName} is const and can't be modified`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ContextType } from "../../../../../core/context/contextType";
|
|
2
|
+
import { OcatError } from "../../coreErrors";
|
|
3
|
+
|
|
4
|
+
export class UndeclaredError extends OcatError {
|
|
5
|
+
constructor(varName: string, type: ContextType) {
|
|
6
|
+
super(`Undeclared ${type.toLowerCase()}`);
|
|
7
|
+
this.message = `${type} ${varName} is not declared`;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class UndeclaredVariableError extends UndeclaredError {
|
|
12
|
+
constructor(varName: string) {
|
|
13
|
+
super(varName, ContextType.Variable);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class UndeclaredFunctionError extends UndeclaredError {
|
|
18
|
+
constructor(varName: string) {
|
|
19
|
+
super(varName, ContextType.Function);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SyntaxError } from "./syntaxErrors.js";
|
|
2
|
+
|
|
3
|
+
export class UndefinedToken extends SyntaxError {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(`Undefined token`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class UnexpectedToken extends SyntaxError {
|
|
10
|
+
constructor(token: string) {
|
|
11
|
+
super(`Unexpected token: ${token}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Enable latest features
|
|
4
|
+
"lib": ["ESNext", "DOM"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
// Best practices
|
|
18
|
+
"strict": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
|
|
22
|
+
// Some stricter flags (disabled by default)
|
|
23
|
+
"noUnusedLocals": false,
|
|
24
|
+
"noUnusedParameters": false,
|
|
25
|
+
"noPropertyAccessFromIndexSignature": false
|
|
26
|
+
}
|
|
27
|
+
}
|