kopscript 0.9.0 → 0.10.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 +7 -0
- package/dist/lexer.js +51 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -831,6 +831,13 @@ functions/classes.
|
|
|
831
831
|
language tour above) — the ceiling that's left is what's *inside* those: no async lambdas,
|
|
832
832
|
and no way to build a `task` value by hand outside an `async` function body.
|
|
833
833
|
|
|
834
|
+
No formatter (`ks fmt`) yet — it was blocked on the lexer discarding every comment entirely,
|
|
835
|
+
which an AST-based pretty-printer would otherwise silently delete on reformat. That's now
|
|
836
|
+
fixed: every `Token` carries its own `leadingComments` (see `src/tokens.ts`'s
|
|
837
|
+
`CommentTrivia`), purely additive trivia the parser/checker/codegen never look at. This is
|
|
838
|
+
groundwork, not the formatter itself — `ks fmt` (walking the AST and re-emitting source with
|
|
839
|
+
comments placed back correctly) is a separate, not-yet-started piece of work.
|
|
840
|
+
|
|
834
841
|
### A frontend framework: Kopular
|
|
835
842
|
|
|
836
843
|
The language shape — modules, closures, DOM interop, `state<T>`, and `extern`/`virtual`
|
package/dist/lexer.js
CHANGED
|
@@ -52,6 +52,10 @@ export class Lexer {
|
|
|
52
52
|
this.pos = 0;
|
|
53
53
|
this.line = 1;
|
|
54
54
|
this.col = 1;
|
|
55
|
+
// A comment before the very first real token has no "previous token" to
|
|
56
|
+
// share a line with — without this, skipTrivia()'s default would
|
|
57
|
+
// incorrectly mark a file-opening comment as sameLineAsPreviousToken.
|
|
58
|
+
this.hasEmittedToken = false;
|
|
55
59
|
}
|
|
56
60
|
tokenize() {
|
|
57
61
|
const tokens = [];
|
|
@@ -64,25 +68,46 @@ export class Lexer {
|
|
|
64
68
|
return tokens;
|
|
65
69
|
}
|
|
66
70
|
next() {
|
|
67
|
-
this.skipTrivia();
|
|
71
|
+
const leadingComments = this.skipTrivia();
|
|
68
72
|
const line = this.line;
|
|
69
73
|
const col = this.col;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
74
|
+
let token;
|
|
75
|
+
if (this.isAtEnd()) {
|
|
76
|
+
token = this.make(TokenKind.EOF, "", line, col);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const c = this.peek();
|
|
80
|
+
if (c === '"')
|
|
81
|
+
token = this.readString(line, col);
|
|
82
|
+
else if (c === "$" && this.peek(1) === '"')
|
|
83
|
+
token = this.readInterpolatedString(line, col);
|
|
84
|
+
else if (c === "r" && this.peek(1) === '"')
|
|
85
|
+
token = this.readRegexLiteral(line, col);
|
|
86
|
+
else if (this.isDigit(c))
|
|
87
|
+
token = this.readNumber(line, col);
|
|
88
|
+
else if (this.isIdentStart(c))
|
|
89
|
+
token = this.readIdentifier(line, col);
|
|
90
|
+
else
|
|
91
|
+
token = this.readOperator(line, col);
|
|
92
|
+
}
|
|
93
|
+
// Concatenated, not overwritten: `readOperator`'s own error-recovery
|
|
94
|
+
// path recursively calls `next()` on an illegal character, and that
|
|
95
|
+
// inner call's `leadingComments` (comments found *after* the bad
|
|
96
|
+
// character) would otherwise be discarded here in favor of this
|
|
97
|
+
// (outer) call's own — comments found *before* it.
|
|
98
|
+
token.leadingComments = [...leadingComments, ...token.leadingComments];
|
|
99
|
+
this.hasEmittedToken = true;
|
|
100
|
+
return token;
|
|
84
101
|
}
|
|
102
|
+
// Comments are otherwise-inert trivia (see tokens.ts's `CommentTrivia`) —
|
|
103
|
+
// this only decides which token each one attaches to as a "leading"
|
|
104
|
+
// comment, and whether it looks trailing-style (`sameLineAsPreviousToken`)
|
|
105
|
+
// for a future formatter to decide how to place it. Only `//` line
|
|
106
|
+
// comments exist in the language; there's no block-comment syntax to
|
|
107
|
+
// handle here.
|
|
85
108
|
skipTrivia() {
|
|
109
|
+
const comments = [];
|
|
110
|
+
let sameLineAsPreviousToken = this.hasEmittedToken;
|
|
86
111
|
for (;;) {
|
|
87
112
|
const c = this.peek();
|
|
88
113
|
if (c === " " || c === "\t" || c === "\r") {
|
|
@@ -90,15 +115,24 @@ export class Lexer {
|
|
|
90
115
|
}
|
|
91
116
|
else if (c === "\n") {
|
|
92
117
|
this.advance();
|
|
118
|
+
sameLineAsPreviousToken = false;
|
|
93
119
|
}
|
|
94
120
|
else if (c === "/" && this.peek(1) === "/") {
|
|
121
|
+
const line = this.line;
|
|
122
|
+
const col = this.col;
|
|
123
|
+
this.advance();
|
|
124
|
+
this.advance();
|
|
125
|
+
let text = "";
|
|
95
126
|
while (!this.isAtEnd() && this.peek() !== "\n")
|
|
96
|
-
this.advance();
|
|
127
|
+
text += this.advance();
|
|
128
|
+
comments.push({ text, line, col, sameLineAsPreviousToken });
|
|
129
|
+
sameLineAsPreviousToken = false; // a `//` comment always eats to end of line
|
|
97
130
|
}
|
|
98
131
|
else {
|
|
99
132
|
break;
|
|
100
133
|
}
|
|
101
134
|
}
|
|
135
|
+
return comments;
|
|
102
136
|
}
|
|
103
137
|
readString(line, col) {
|
|
104
138
|
this.advance(); // opening quote
|
|
@@ -288,7 +322,7 @@ export class Lexer {
|
|
|
288
322
|
return this.next();
|
|
289
323
|
}
|
|
290
324
|
make(kind, lexeme, line, col) {
|
|
291
|
-
return { kind, lexeme, line, col };
|
|
325
|
+
return { kind, lexeme, line, col, leadingComments: [] };
|
|
292
326
|
}
|
|
293
327
|
isAtEnd() {
|
|
294
328
|
return this.pos >= this.source.length;
|
package/package.json
CHANGED