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.
Files changed (3) hide show
  1. package/README.md +7 -0
  2. package/dist/lexer.js +51 -17
  3. 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
- if (this.isAtEnd())
71
- return this.make(TokenKind.EOF, "", line, col);
72
- const c = this.peek();
73
- if (c === '"')
74
- return this.readString(line, col);
75
- if (c === "$" && this.peek(1) === '"')
76
- return this.readInterpolatedString(line, col);
77
- if (c === "r" && this.peek(1) === '"')
78
- return this.readRegexLiteral(line, col);
79
- if (this.isDigit(c))
80
- return this.readNumber(line, col);
81
- if (this.isIdentStart(c))
82
- return this.readIdentifier(line, col);
83
- return this.readOperator(line, col);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kopscript",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "KopScript: a small OOP, strongly-typed language that transpiles to JavaScript, with generics and nullable types",
5
5
  "type": "module",
6
6
  "license": "MIT",