Download unified-engine/kernel/compiler/lexer.js from Snapkitty/repoverse: direct link, hf CLI and curl.
- Browser
- Download file 4.59 kB
-
https://huggingface.co/Snapkitty/repoverse/resolve/main/unified-engine/kernel/compiler/lexer.js
- Command line
-
hf download hf://Snapkitty/repoverse/unified-engine/kernel/compiler/lexer.js
-
curl -L -o lexer.js https://huggingface.co/Snapkitty/repoverse/resolve/main/unified-engine/kernel/compiler/lexer.js
4.59 kB
| /** | |
| * Lexer for the CUDA-like kernel language. | |
| * | |
| * Emits a token stream with source offsets so parser and lowering errors can point | |
| * at a line. Whitespace and comments are skipped; nothing is evaluated here. | |
| */ | |
| export const TokenType = { | |
| IDENT: 'ident', | |
| KEYWORD: 'keyword', | |
| INT: 'int', | |
| FLOAT: 'float', | |
| PUNCT: 'punct', | |
| EOF: 'eof', | |
| }; | |
| const KEYWORDS = new Set([ | |
| '__global__', | |
| '__device__', | |
| '__shared__', | |
| 'void', | |
| 'int', | |
| 'uint', | |
| 'float', | |
| 'double', | |
| 'const', | |
| 'return', | |
| 'if', | |
| 'else', | |
| 'while', | |
| 'for', | |
| 'break', | |
| 'continue', | |
| 'true', | |
| 'false', | |
| ]); | |
| /** Multi-character operators are matched before single-character ones. */ | |
| const PUNCTUATORS = [ | |
| '&&', '||', '==', '!=', '<=', '>=', '<<', '>>', '+=', '-=', '*=', '/=', '%=', | |
| '++', '--', '->', | |
| '{', '}', '(', ')', '[', ']', ';', ',', '<', '>', '=', '+', '-', '*', '/', '%', '!', '&', '|', '^', '~', '.', | |
| ]; | |
| export class LexError extends Error { | |
| constructor(message, line, column) { | |
| super(`${message} (line ${line}, column ${column})`); | |
| this.name = 'LexError'; | |
| this.line = line; | |
| this.column = column; | |
| } | |
| } | |
| const isDigit = (c) => c >= '0' && c <= '9'; | |
| const isIdentStart = (c) => /[A-Za-z_]/.test(c); | |
| const isIdentPart = (c) => /[A-Za-z0-9_]/.test(c); | |
| export function tokenize(source) { | |
| const tokens = []; | |
| let i = 0; | |
| let line = 1; | |
| let lineStart = 0; | |
| const column = (offset) => offset - lineStart + 1; | |
| while (i < source.length) { | |
| const c = source[i]; | |
| if (c === '\n') { | |
| i += 1; | |
| line += 1; | |
| lineStart = i; | |
| continue; | |
| } | |
| if (c === ' ' || c === '\t' || c === '\r') { | |
| i += 1; | |
| continue; | |
| } | |
| // Comments run to end of line. | |
| if (c === '/' && source[i + 1] === '/') { | |
| while (i < source.length && source[i] !== '\n') i += 1; | |
| continue; | |
| } | |
| if (c === '/' && source[i + 1] === '*') { | |
| const startLine = line; | |
| const startColumn = column(i); | |
| i += 2; | |
| for (;;) { | |
| if (i >= source.length) throw new LexError('Unterminated block comment', startLine, startColumn); | |
| if (source[i] === '\n') { | |
| line += 1; | |
| i += 1; | |
| lineStart = i; | |
| continue; | |
| } | |
| if (source[i] === '*' && source[i + 1] === '/') { | |
| i += 2; | |
| break; | |
| } | |
| i += 1; | |
| } | |
| continue; | |
| } | |
| // Identifiers, keywords and __global__-style qualifiers. | |
| if (isIdentStart(c)) { | |
| const start = i; | |
| while (i < source.length && isIdentPart(source[i])) i += 1; | |
| const value = source.slice(start, i); | |
| tokens.push({ | |
| type: KEYWORDS.has(value) ? TokenType.KEYWORD : TokenType.IDENT, | |
| value, | |
| line, | |
| column: column(start), | |
| offset: start, | |
| }); | |
| continue; | |
| } | |
| // Numbers: decimal and 0x hex, with optional fractional part and f suffix. | |
| if (isDigit(c) || (c === '.' && isDigit(source[i + 1]))) { | |
| const start = i; | |
| let isFloat = false; | |
| if (c === '0' && (source[i + 1] === 'x' || source[i + 1] === 'X')) { | |
| i += 2; | |
| while (i < source.length && /[0-9a-fA-F]/.test(source[i])) i += 1; | |
| } else { | |
| while (i < source.length && isDigit(source[i])) i += 1; | |
| if (source[i] === '.') { | |
| isFloat = true; | |
| i += 1; | |
| while (i < source.length && isDigit(source[i])) i += 1; | |
| } | |
| if (source[i] === 'e' || source[i] === 'E') { | |
| isFloat = true; | |
| i += 1; | |
| if (source[i] === '+' || source[i] === '-') i += 1; | |
| while (i < source.length && isDigit(source[i])) i += 1; | |
| } | |
| } | |
| if (source[i] === 'f' || source[i] === 'F') { | |
| isFloat = true; | |
| i += 1; | |
| } | |
| const text = source.slice(start, i); | |
| if (i < source.length && isIdentStart(source[i])) { | |
| throw new LexError(`Malformed numeric literal '${text}${source[i]}'`, line, column(start)); | |
| } | |
| tokens.push({ type: isFloat ? TokenType.FLOAT : TokenType.INT, value: text, line, column: column(start), offset: start }); | |
| continue; | |
| } | |
| // Punctuators, longest match first. | |
| const punct = PUNCTUATORS.find((p) => source.startsWith(p, i)); | |
| if (punct) { | |
| const start = i; | |
| i += punct.length; | |
| tokens.push({ type: TokenType.PUNCT, value: punct, line, column: column(start), offset: start }); | |
| continue; | |
| } | |
| throw new LexError(`Unexpected character '${c}'`, line, column(i)); | |
| } | |
| tokens.push({ type: TokenType.EOF, value: '<end of input>', line, column: column(i), offset: i }); | |
| return tokens; | |
| } | |