nava-sanskritam 1.0.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/Interpreter.js +100 -0
- package/Lexer.js +154 -0
- package/Parser.js +268 -0
- package/README.md +77 -0
- package/bin/nava.js +50 -0
- package/package.json +28 -0
- package/sankalpa.ns +13 -0
package/Interpreter.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
class Interpreter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.environment = new Map();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interpret(ast) {
|
|
7
|
+
if (ast.type === 'Program') {
|
|
8
|
+
for (let stmt of ast.body) {
|
|
9
|
+
this.execute(stmt);
|
|
10
|
+
}
|
|
11
|
+
} else {
|
|
12
|
+
throw new Error(`Unknown AST node type: ${ast.type}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
execute(stmt) {
|
|
17
|
+
switch (stmt.type) {
|
|
18
|
+
case 'VariableDeclaration':
|
|
19
|
+
let val = this.evaluate(stmt.init);
|
|
20
|
+
this.environment.set(stmt.id, val);
|
|
21
|
+
break;
|
|
22
|
+
case 'PrintStatement':
|
|
23
|
+
console.log(this.evaluate(stmt.expression));
|
|
24
|
+
break;
|
|
25
|
+
case 'ExpressionStatement':
|
|
26
|
+
this.evaluate(stmt.expression);
|
|
27
|
+
break;
|
|
28
|
+
case 'IfStatement':
|
|
29
|
+
if (this.evaluate(stmt.condition)) {
|
|
30
|
+
this.executeBlockOrStatement(stmt.consequence);
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
case 'LoopStatement':
|
|
34
|
+
if (stmt.init) {
|
|
35
|
+
this.evaluate(stmt.init);
|
|
36
|
+
}
|
|
37
|
+
while (stmt.test ? this.evaluate(stmt.test) : true) {
|
|
38
|
+
this.executeBlockOrStatement(stmt.body);
|
|
39
|
+
if (stmt.update) {
|
|
40
|
+
this.evaluate(stmt.update);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
case 'BlockStatement':
|
|
45
|
+
for (let bStmt of stmt.body) {
|
|
46
|
+
this.execute(bStmt);
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
throw new Error(`Unknown statement type: ${stmt.type}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
executeBlockOrStatement(node) {
|
|
55
|
+
if (node.type === 'BlockStatement') {
|
|
56
|
+
this.execute(node);
|
|
57
|
+
} else {
|
|
58
|
+
this.execute(node);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
evaluate(expr) {
|
|
63
|
+
switch (expr.type) {
|
|
64
|
+
case 'Literal':
|
|
65
|
+
return expr.value;
|
|
66
|
+
case 'Identifier':
|
|
67
|
+
if (this.environment.has(expr.name)) {
|
|
68
|
+
return this.environment.get(expr.name);
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Undefined variable: ${expr.name}`);
|
|
71
|
+
case 'Assignment':
|
|
72
|
+
let value = this.evaluate(expr.right);
|
|
73
|
+
// Allow assignment to implicitly declare if not present in loops, but strict generally
|
|
74
|
+
// Actually, for the loop like `i = 1`, if it's not declared with अस्ति, we should allow it as implicit declaration or simply set it.
|
|
75
|
+
this.environment.set(expr.left, value);
|
|
76
|
+
return value;
|
|
77
|
+
case 'BinaryExpression':
|
|
78
|
+
let left = this.evaluate(expr.left);
|
|
79
|
+
let right = this.evaluate(expr.right);
|
|
80
|
+
switch (expr.operator) {
|
|
81
|
+
case '+': return left + right;
|
|
82
|
+
case '-': return left - right;
|
|
83
|
+
case '*': return left * right;
|
|
84
|
+
case '/': return left / right;
|
|
85
|
+
case '<': return left < right;
|
|
86
|
+
case '<=': return left <= right;
|
|
87
|
+
case '>': return left > right;
|
|
88
|
+
case '>=': return left >= right;
|
|
89
|
+
case '==': return left === right;
|
|
90
|
+
case '!=': return left !== right;
|
|
91
|
+
default:
|
|
92
|
+
throw new Error(`Unknown operator: ${expr.operator}`);
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
throw new Error(`Unknown expression type: ${expr.type}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = Interpreter;
|
package/Lexer.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
class Lexer {
|
|
2
|
+
constructor(input) {
|
|
3
|
+
this.input = input;
|
|
4
|
+
this.pos = 0;
|
|
5
|
+
this.line = 1;
|
|
6
|
+
this.col = 1;
|
|
7
|
+
this.tokens = [];
|
|
8
|
+
|
|
9
|
+
this.keywords = new Set(['अस्ति', 'वद', 'यदि', 'तर्हि', 'चक्र']);
|
|
10
|
+
this.devanagariDigits = {
|
|
11
|
+
'०': '0', '१': '1', '२': '2', '३': '3', '४': '4',
|
|
12
|
+
'५': '5', '६': '6', '७': '7', '८': '8', '९': '9'
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
tokenize() {
|
|
17
|
+
while (this.pos < this.input.length) {
|
|
18
|
+
let char = this.currentChar();
|
|
19
|
+
|
|
20
|
+
if (this.isWhitespace(char)) {
|
|
21
|
+
this.advance();
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (this.isPunctuation(char)) {
|
|
26
|
+
this.tokens.push({ type: 'PUNCTUATION', value: char, line: this.line, col: this.col });
|
|
27
|
+
this.advance();
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (this.isOperatorChar(char)) {
|
|
32
|
+
let op = this.readOperator();
|
|
33
|
+
this.tokens.push({ type: 'OPERATOR', value: op, line: this.line, col: this.col });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (char === '"' || char === "'") {
|
|
38
|
+
let str = this.readString(char);
|
|
39
|
+
this.tokens.push({ type: 'STRING', value: str, line: this.line, col: this.col });
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (this.isDigit(char) || this.isDevanagariDigit(char)) {
|
|
44
|
+
let num = this.readNumber();
|
|
45
|
+
this.tokens.push({ type: 'NUMBER', value: num, line: this.line, col: this.col });
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (this.isIdentifierChar(char)) {
|
|
50
|
+
let id = this.readIdentifier();
|
|
51
|
+
if (this.keywords.has(id)) {
|
|
52
|
+
this.tokens.push({ type: 'KEYWORD', value: id, line: this.line, col: this.col });
|
|
53
|
+
} else {
|
|
54
|
+
this.tokens.push({ type: 'IDENTIFIER', value: id, line: this.line, col: this.col });
|
|
55
|
+
}
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw new Error(`Lexer Error: Unexpected character '${char}' at line ${this.line}, col ${this.col}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.tokens.push({ type: 'EOF', value: null, line: this.line, col: this.col });
|
|
63
|
+
return this.tokens;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
currentChar() {
|
|
67
|
+
return this.input[this.pos];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
advance() {
|
|
71
|
+
if (this.currentChar() === '\n') {
|
|
72
|
+
this.line++;
|
|
73
|
+
this.col = 1;
|
|
74
|
+
} else {
|
|
75
|
+
this.col++;
|
|
76
|
+
}
|
|
77
|
+
this.pos++;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
isWhitespace(char) {
|
|
81
|
+
return /\s/.test(char);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
isPunctuation(char) {
|
|
85
|
+
return /[(){}\[\],;]/.test(char);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
isOperatorChar(char) {
|
|
89
|
+
return /[=+\-*/<>!]/.test(char);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
readOperator() {
|
|
93
|
+
let op = '';
|
|
94
|
+
while (this.pos < this.input.length && this.isOperatorChar(this.currentChar())) {
|
|
95
|
+
op += this.currentChar();
|
|
96
|
+
this.advance();
|
|
97
|
+
}
|
|
98
|
+
return op;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
readString(quoteChar) {
|
|
102
|
+
let startLine = this.line;
|
|
103
|
+
let startCol = this.col;
|
|
104
|
+
this.advance(); // Skip opening quote
|
|
105
|
+
let str = '';
|
|
106
|
+
while (this.pos < this.input.length && this.currentChar() !== quoteChar) {
|
|
107
|
+
str += this.currentChar();
|
|
108
|
+
this.advance();
|
|
109
|
+
}
|
|
110
|
+
if (this.pos >= this.input.length) {
|
|
111
|
+
throw new Error(`Lexer Error: Unterminated string starting at line ${startLine}, col ${startCol}`);
|
|
112
|
+
}
|
|
113
|
+
this.advance(); // Skip closing quote
|
|
114
|
+
return str;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
isDigit(char) {
|
|
118
|
+
return /[0-9]/.test(char);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
isDevanagariDigit(char) {
|
|
122
|
+
return char in this.devanagariDigits;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
readNumber() {
|
|
126
|
+
let numStr = '';
|
|
127
|
+
while (this.pos < this.input.length && (this.isDigit(this.currentChar()) || this.isDevanagariDigit(this.currentChar()))) {
|
|
128
|
+
let char = this.currentChar();
|
|
129
|
+
if (this.isDevanagariDigit(char)) {
|
|
130
|
+
numStr += this.devanagariDigits[char];
|
|
131
|
+
} else {
|
|
132
|
+
numStr += char;
|
|
133
|
+
}
|
|
134
|
+
this.advance();
|
|
135
|
+
}
|
|
136
|
+
return parseFloat(numStr);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
isIdentifierChar(char) {
|
|
140
|
+
// Matches letters from any language, numbers, marks (like Devanagari matras/halants), or underscore
|
|
141
|
+
return /[\p{L}\p{N}\p{M}_]/u.test(char) && !this.isPunctuation(char) && !this.isOperatorChar(char) && !this.isWhitespace(char);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
readIdentifier() {
|
|
145
|
+
let id = '';
|
|
146
|
+
while (this.pos < this.input.length && this.isIdentifierChar(this.currentChar())) {
|
|
147
|
+
id += this.currentChar();
|
|
148
|
+
this.advance();
|
|
149
|
+
}
|
|
150
|
+
return id;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = Lexer;
|
package/Parser.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
class Parser {
|
|
2
|
+
constructor(tokens) {
|
|
3
|
+
this.tokens = tokens;
|
|
4
|
+
this.pos = 0;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
parse() {
|
|
8
|
+
let ast = { type: 'Program', body: [] };
|
|
9
|
+
while (!this.isAtEnd()) {
|
|
10
|
+
ast.body.push(this.parseStatement());
|
|
11
|
+
}
|
|
12
|
+
return ast;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
parseStatement() {
|
|
16
|
+
if (this.match('KEYWORD', 'अस्ति')) return this.parseVariableDeclaration();
|
|
17
|
+
if (this.match('KEYWORD', 'वद')) return this.parsePrintStatement();
|
|
18
|
+
if (this.match('KEYWORD', 'यदि')) return this.parseIfStatement();
|
|
19
|
+
if (this.match('KEYWORD', 'चक्र')) return this.parseLoopStatement();
|
|
20
|
+
|
|
21
|
+
return this.parseExpressionStatement();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
parseVariableDeclaration() {
|
|
25
|
+
// अस्ति नाम = "आचार्य";
|
|
26
|
+
let idToken = this.consume('IDENTIFIER', "Expected variable name after 'अस्ति'.");
|
|
27
|
+
this.consume('OPERATOR', "Expected '=' after variable name.", '=');
|
|
28
|
+
let init = this.parseExpression();
|
|
29
|
+
this.consume('PUNCTUATION', "Expected ';' after variable declaration.", ';');
|
|
30
|
+
return {
|
|
31
|
+
type: 'VariableDeclaration',
|
|
32
|
+
id: idToken.value,
|
|
33
|
+
init: init
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
parsePrintStatement() {
|
|
38
|
+
// वद("सत्यम्");
|
|
39
|
+
this.consume('PUNCTUATION', "Expected '(' after 'वद'.", '(');
|
|
40
|
+
let expression = this.parseExpression();
|
|
41
|
+
this.consume('PUNCTUATION', "Expected ')' after print expression.", ')');
|
|
42
|
+
this.consume('PUNCTUATION', "Expected ';' after print statement.", ';');
|
|
43
|
+
return {
|
|
44
|
+
type: 'PrintStatement',
|
|
45
|
+
expression: expression
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
parseIfStatement() {
|
|
50
|
+
// यदि (क > १०) तर्हि वद("सत्यम्");
|
|
51
|
+
this.consume('PUNCTUATION', "Expected '(' after 'यदि'.", '(');
|
|
52
|
+
let condition = this.parseExpression();
|
|
53
|
+
this.consume('PUNCTUATION', "Expected ')' after condition.", ')');
|
|
54
|
+
|
|
55
|
+
this.consume('KEYWORD', "Expected 'तर्हि' after condition in if statement.", 'तर्हि');
|
|
56
|
+
|
|
57
|
+
// Single statement or block
|
|
58
|
+
let consequence;
|
|
59
|
+
if (this.check('PUNCTUATION', '{')) {
|
|
60
|
+
consequence = this.parseBlock();
|
|
61
|
+
} else {
|
|
62
|
+
consequence = this.parseStatement();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
type: 'IfStatement',
|
|
67
|
+
condition: condition,
|
|
68
|
+
consequence: consequence
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
parseLoopStatement() {
|
|
73
|
+
// चक्र (i = १; i < ५) { ... }
|
|
74
|
+
this.consume('PUNCTUATION', "Expected '(' after 'चक्र'.", '(');
|
|
75
|
+
|
|
76
|
+
let init = null;
|
|
77
|
+
if (!this.check('PUNCTUATION', ';')) {
|
|
78
|
+
if (this.check('IDENTIFIER')) {
|
|
79
|
+
let idToken = this.consume('IDENTIFIER');
|
|
80
|
+
this.consume('OPERATOR', "Expected '=' in loop initialization.", '=');
|
|
81
|
+
let value = this.parseExpression();
|
|
82
|
+
init = { type: 'Assignment', left: idToken.value, right: value };
|
|
83
|
+
} else {
|
|
84
|
+
throw new Error("Invalid initialization in loop.");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
this.consume('PUNCTUATION', "Expected ';' after loop initialization.", ';');
|
|
88
|
+
|
|
89
|
+
let test = null;
|
|
90
|
+
if (!this.check('PUNCTUATION', ';')) {
|
|
91
|
+
test = this.parseExpression();
|
|
92
|
+
}
|
|
93
|
+
this.consume('PUNCTUATION', "Expected ';' after loop condition.", ';');
|
|
94
|
+
|
|
95
|
+
// Handling post-increment (e.g. i = i + 1 as currently parsing assignment might need special parsing)
|
|
96
|
+
// For simplicity based on specs, let's say the update might be missing or parsed as assignment
|
|
97
|
+
let update = null;
|
|
98
|
+
if (!this.check('PUNCTUATION', ')')) {
|
|
99
|
+
if (this.check('IDENTIFIER')) {
|
|
100
|
+
let idToken = this.consume('IDENTIFIER');
|
|
101
|
+
this.consume('OPERATOR', "Expected '=' in loop update.", '=');
|
|
102
|
+
let value = this.parseExpression();
|
|
103
|
+
update = { type: 'Assignment', left: idToken.value, right: value };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
this.consume('PUNCTUATION', "Expected ')' after loop update.", ')');
|
|
107
|
+
|
|
108
|
+
let body;
|
|
109
|
+
if (this.check('PUNCTUATION', '{')) {
|
|
110
|
+
body = this.parseBlock();
|
|
111
|
+
} else {
|
|
112
|
+
body = { type: 'BlockStatement', body: [this.parseStatement()] };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
type: 'LoopStatement',
|
|
117
|
+
init: init,
|
|
118
|
+
test: test,
|
|
119
|
+
update: update,
|
|
120
|
+
body: body
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
parseExpressionStatement() {
|
|
125
|
+
// e.g. a = 5;
|
|
126
|
+
if (this.check('IDENTIFIER')) {
|
|
127
|
+
let nextToken = this.tokens[this.pos + 1];
|
|
128
|
+
if (nextToken && nextToken.type === 'OPERATOR' && nextToken.value === '=') {
|
|
129
|
+
let idToken = this.consume('IDENTIFIER');
|
|
130
|
+
this.consume('OPERATOR', "Expected '='", '=');
|
|
131
|
+
let right = this.parseExpression();
|
|
132
|
+
this.consume('PUNCTUATION', "Expected ';'", ';');
|
|
133
|
+
return {
|
|
134
|
+
type: 'ExpressionStatement',
|
|
135
|
+
expression: {
|
|
136
|
+
type: 'Assignment',
|
|
137
|
+
left: idToken.value,
|
|
138
|
+
right: right
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let expr = this.parseExpression();
|
|
145
|
+
this.consume('PUNCTUATION', "Expected ';' after expression.", ';');
|
|
146
|
+
return {
|
|
147
|
+
type: 'ExpressionStatement',
|
|
148
|
+
expression: expr
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
parseBlock() {
|
|
153
|
+
this.consume('PUNCTUATION', "Expected '{'.", '{');
|
|
154
|
+
let statements = [];
|
|
155
|
+
while (!this.check('PUNCTUATION', '}') && !this.isAtEnd()) {
|
|
156
|
+
statements.push(this.parseStatement());
|
|
157
|
+
}
|
|
158
|
+
this.consume('PUNCTUATION', "Expected '}'.", '}');
|
|
159
|
+
return {
|
|
160
|
+
type: 'BlockStatement',
|
|
161
|
+
body: statements
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
parseExpression() {
|
|
166
|
+
return this.parseEquality();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
parseEquality() {
|
|
170
|
+
let expr = this.parseRelational();
|
|
171
|
+
while (this.match('OPERATOR', '==') || this.match('OPERATOR', '!=')) {
|
|
172
|
+
let operator = this.previous().value;
|
|
173
|
+
let right = this.parseRelational();
|
|
174
|
+
expr = { type: 'BinaryExpression', operator, left: expr, right };
|
|
175
|
+
}
|
|
176
|
+
return expr;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
parseRelational() {
|
|
180
|
+
let expr = this.parseAdditive();
|
|
181
|
+
while (this.match('OPERATOR', '<') || this.match('OPERATOR', '<=') ||
|
|
182
|
+
this.match('OPERATOR', '>') || this.match('OPERATOR', '>=')) {
|
|
183
|
+
let operator = this.previous().value;
|
|
184
|
+
let right = this.parseAdditive();
|
|
185
|
+
expr = { type: 'BinaryExpression', operator, left: expr, right };
|
|
186
|
+
}
|
|
187
|
+
return expr;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
parseAdditive() {
|
|
191
|
+
let expr = this.parseMultiplicative();
|
|
192
|
+
while (this.match('OPERATOR', '+') || this.match('OPERATOR', '-')) {
|
|
193
|
+
let operator = this.previous().value;
|
|
194
|
+
let right = this.parseMultiplicative();
|
|
195
|
+
expr = { type: 'BinaryExpression', operator, left: expr, right };
|
|
196
|
+
}
|
|
197
|
+
return expr;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
parseMultiplicative() {
|
|
201
|
+
let expr = this.parsePrimary();
|
|
202
|
+
while (this.match('OPERATOR', '*') || this.match('OPERATOR', '/')) {
|
|
203
|
+
let operator = this.previous().value;
|
|
204
|
+
let right = this.parsePrimary();
|
|
205
|
+
expr = { type: 'BinaryExpression', operator, left: expr, right };
|
|
206
|
+
}
|
|
207
|
+
return expr;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
parsePrimary() {
|
|
211
|
+
if (this.match('NUMBER')) {
|
|
212
|
+
return { type: 'Literal', value: this.previous().value };
|
|
213
|
+
}
|
|
214
|
+
if (this.match('STRING')) {
|
|
215
|
+
return { type: 'Literal', value: this.previous().value };
|
|
216
|
+
}
|
|
217
|
+
if (this.match('IDENTIFIER')) {
|
|
218
|
+
return { type: 'Identifier', name: this.previous().value };
|
|
219
|
+
}
|
|
220
|
+
if (this.match('PUNCTUATION', '(')) {
|
|
221
|
+
let expr = this.parseExpression();
|
|
222
|
+
this.consume('PUNCTUATION', "Expected ')' after expression.", ')');
|
|
223
|
+
return expr;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
throw new Error(`Parser Error: Unexpected token '${this.peek().value}' at line ${this.peek().line}, col ${this.peek().col}`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
match(type, value = null) {
|
|
230
|
+
if (this.check(type, value)) {
|
|
231
|
+
this.advance();
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
check(type, value = null) {
|
|
238
|
+
if (this.isAtEnd()) return false;
|
|
239
|
+
let token = this.peek();
|
|
240
|
+
if (token.type !== type) return false;
|
|
241
|
+
if (value !== null && token.value !== value) return false;
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
advance() {
|
|
246
|
+
if (!this.isAtEnd()) this.pos++;
|
|
247
|
+
return this.previous();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
isAtEnd() {
|
|
251
|
+
return this.peek().type === 'EOF';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
peek() {
|
|
255
|
+
return this.tokens[this.pos];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
previous() {
|
|
259
|
+
return this.tokens[this.pos - 1];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
consume(type, message, value = null) {
|
|
263
|
+
if (this.check(type, value)) return this.advance();
|
|
264
|
+
throw new Error(`Parser Error: ${message} Found '${this.peek().value}' at line ${this.peek().line}, col ${this.peek().col}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
module.exports = Parser;
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Nava Sanskritam (नव संस्कृतम्)
|
|
2
|
+
|
|
3
|
+
A professional, high-performance programming language built on Panini's Ashtadhyayi logic, created by **NavaSanganakah Multiventures**.
|
|
4
|
+
|
|
5
|
+
## Vedic Vision
|
|
6
|
+
|
|
7
|
+
At NavaSanganakah Multiventures, we blend the ancient intelligence of Vedic sciences with modern computation. Panini's grammatical rules in Ashtadhyayi lay out a complete, unambiguous algorithmic framework that predates modern computer science by millennia. Nava Sanskritam is our proprietary, production-ready execution engine designed to bring this profound logic directly into modern software development, providing developers with a robust, modular, and performant AST-based interpreter.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
You can install the Nava CLI globally on your local machine using `npm link` to map the `nava` command to your environment:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Clone the repository
|
|
15
|
+
git clone <repository-url>
|
|
16
|
+
cd nava
|
|
17
|
+
|
|
18
|
+
# Install dependencies
|
|
19
|
+
npm install
|
|
20
|
+
|
|
21
|
+
# Link the package globally
|
|
22
|
+
npm link
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
After installation, you can use the command globally:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
nava run yourfile.ns
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Syntax Specs
|
|
32
|
+
|
|
33
|
+
Nava Sanskritam supports a robust core of programmatic concepts using Devanagari numerals and Sanskrit keywords.
|
|
34
|
+
|
|
35
|
+
### Variable Declaration (अस्ति)
|
|
36
|
+
Use `अस्ति` to declare variables.
|
|
37
|
+
|
|
38
|
+
```sanskrit
|
|
39
|
+
अस्ति नाम = "आचार्य";
|
|
40
|
+
अस्ति क = १०;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Output (वद)
|
|
44
|
+
Use `वद` to print expressions to the console.
|
|
45
|
+
|
|
46
|
+
```sanskrit
|
|
47
|
+
वद("सत्यम्");
|
|
48
|
+
वद(नाम);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Conditionals (यदि-तर्हि)
|
|
52
|
+
Use `यदि` for conditions and `तर्हि` for execution blocks if the condition is met.
|
|
53
|
+
|
|
54
|
+
```sanskrit
|
|
55
|
+
यदि (क > ५) तर्हि वद("सत्यम्");
|
|
56
|
+
|
|
57
|
+
यदि (क > ५) तर्हि {
|
|
58
|
+
वद("सत्यम्");
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Loops (चक्र)
|
|
63
|
+
Use `चक्र` to create iterative loops using standard or Devanagari numerals.
|
|
64
|
+
|
|
65
|
+
```sanskrit
|
|
66
|
+
चक्र (i = १; i < ५; i = i + १) {
|
|
67
|
+
वद(i);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Development and Architecture
|
|
72
|
+
- `Lexer.js`: Robust tokenizer supporting UTF-8 identifiers, Paninian keywords, and Devanagari Numerals.
|
|
73
|
+
- `Parser.js`: Builds an Abstract Syntax Tree (AST) utilizing standard logical bindings and structures.
|
|
74
|
+
- `Interpreter.js`: AST execution engine complete with lexical environment scoping.
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
Proprietary software of NavaSanganakah Multiventures. All Rights Reserved.
|
package/bin/nava.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const Lexer = require('../Lexer');
|
|
8
|
+
const Parser = require('../Parser');
|
|
9
|
+
const Interpreter = require('../Interpreter');
|
|
10
|
+
|
|
11
|
+
program
|
|
12
|
+
.name('nava')
|
|
13
|
+
.description('Nava Sanskritam (Panini\'s Ashtadhyayi logic engine CLI)')
|
|
14
|
+
.version('1.0.0');
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.command('run <file>')
|
|
18
|
+
.description('Execute a Nava Sanskritam (.ns) script')
|
|
19
|
+
.action((file) => {
|
|
20
|
+
try {
|
|
21
|
+
if (!file.endsWith('.ns')) {
|
|
22
|
+
console.error("दोषः (Error): कृपया '.ns' इति विस्तारयुक्तं सञ्चिकां प्रयच्छतु। (Please provide a file with a '.ns' extension.)");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const filePath = path.resolve(process.cwd(), file);
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(filePath)) {
|
|
29
|
+
console.error(`दोषः (Error): सञ्चिका न प्राप्ता। (File not found: ${file})`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const input = fs.readFileSync(filePath, 'utf-8');
|
|
34
|
+
|
|
35
|
+
const lexer = new Lexer(input);
|
|
36
|
+
const tokens = lexer.tokenize();
|
|
37
|
+
|
|
38
|
+
const parser = new Parser(tokens);
|
|
39
|
+
const ast = parser.parse();
|
|
40
|
+
|
|
41
|
+
const interpreter = new Interpreter();
|
|
42
|
+
interpreter.interpret(ast);
|
|
43
|
+
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`दोषः (Error during execution):\n${error.message}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nava-sanskritam",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Nava Sanskritam - A revolutionary programming language based on Panini's Ashtadhyayi logic.",
|
|
5
|
+
"main": "Interpreter.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
|
+
"start": "node bin/nava.js"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"Sanskrit",
|
|
13
|
+
"Vedic",
|
|
14
|
+
"Programming",
|
|
15
|
+
"Ashtadhyayi",
|
|
16
|
+
"NavaSanganakah",
|
|
17
|
+
"Panini"
|
|
18
|
+
],
|
|
19
|
+
"author": "Acharya Pandit Dheerendra Tripathi (NavaSanganakah Multiventures)",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"commander": "^14.0.3"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"nava": "bin/nava.js"
|
|
26
|
+
},
|
|
27
|
+
"private": false
|
|
28
|
+
}
|
package/sankalpa.ns
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
अस्ति आचार्यः = "धीरेन्द्र त्रिपाठी";
|
|
2
|
+
अस्ति मिशनम् = "नवा संस्कृतम्";
|
|
3
|
+
अस्ति सफलता_अंक = १०८;
|
|
4
|
+
|
|
5
|
+
वद(आचार्यः);
|
|
6
|
+
वद(मिशनम्);
|
|
7
|
+
|
|
8
|
+
यदि (सफलता_अंक > १००)
|
|
9
|
+
तर्हि वद("अष्टाध्यायी लॉजिक सक्रिय अस्ति!");
|
|
10
|
+
|
|
11
|
+
चक्र (i = १; i < ४; i = i + १) {
|
|
12
|
+
वद("जयतु संस्कृतम्");
|
|
13
|
+
}
|