diff --git a/src/bin/bolt.ts b/src/bin/bolt.ts index 101569580..49a44cec9 100644 --- a/src/bin/bolt.ts +++ b/src/bin/bolt.ts @@ -2,24 +2,10 @@ import "reflect-metadata" import "source-map-support/register" -import { sync as globSync } from "glob" -import * as path from "path" -import * as fs from "fs" import yargs from "yargs" -import yaml from "js-yaml" -import semver from "semver" -import { Program } from "../program" -import { parseSourceFile } from "../parser" import { Frontend } from "../frontend" -import { Package } from "../common" -import {hasOwnProperty, upsearchSync, expandPath, FastStringMap, assert} from "../util" -import {isString} from "util" -import {DiagnosticPrinter, E_FIELD_NOT_PRESENT, E_FIELD_MUST_BE_BOOLEAN, E_FIELD_MUST_BE_STRING, E_FIELD_HAS_INVALID_VERSION_NUMBER} from "../diagnostics" - -//global.print = function (value: any) { -// console.error(require('util').inspect(value, { depth: Infinity, colors: true })) -//} +import { expandPath } from "../util" const BOLT_HOME = expandPath(process.env['BOLT_HOME'] ?? '~/.bolt-compiler') @@ -30,159 +16,15 @@ function toArray(value: T | T[]): T[] { return value === null || value === undefined ? [] : [value] } -function pushAll(array: T[], elements: T[]) { - for (const element of elements) { - array.push(element); - } -} - -function flatMap(array: T[], proc: (element: T) => T[]) { - let out: T[] = [] - for (const element of array) { - pushAll(out, proc(element)); - } - return out -} - -const diagnostics = new DiagnosticPrinter(); - -function loadPackageMetadata(rootDir: string) { - - let name = null - let version = null; - let autoImport = false; - - let hasVersionErrors = false; - let hasNameErrors = false; - - const filepath = path.join(rootDir, 'Boltfile'); - if (fs.existsSync(filepath)) { - const data = yaml.safeLoad(fs.readFileSync(filepath, 'utf8')); - if (data !== undefined) { - if (hasOwnProperty(data, 'name')) { - if (!isString(data.name)) { - diagnostics.add({ - message: E_FIELD_MUST_BE_STRING, - severity: 'error', - args: { name: 'name' }, - }); - hasNameErrors = true; - } else { - name = data.name; - } - } - if (hasOwnProperty(data, 'version')) { - if (!isString(data.version)) { - diagnostics.add({ - message: E_FIELD_MUST_BE_STRING, - args: { name: 'version' }, - severity: 'error', - }); - hasVersionErrors = true; - } else { - if (!semver.valid(data.version)) { - diagnostics.add({ - message: E_FIELD_HAS_INVALID_VERSION_NUMBER, - args: { name: 'version' }, - severity: 'error', - }); - hasVersionErrors = true; - } else { - version = data.version; - } - } - } - if (hasOwnProperty(data, 'auto-import')) { - if (typeof(data['auto-import']) !== 'boolean') { - diagnostics.add({ - message: E_FIELD_MUST_BE_BOOLEAN, - args: { name: 'auto-import' }, - severity: 'error', - }) - } else { - autoImport = data['auto-import']; - } - } - } - } - - if (name === null && !hasNameErrors) { - diagnostics.add({ - message: E_FIELD_NOT_PRESENT, - severity: 'warning', - args: { name: 'name' }, - }); - } - - if (version === null && !hasVersionErrors) { - diagnostics.add({ - message: E_FIELD_NOT_PRESENT, - severity: 'warning', - args: { name: 'version' }, - }); - } - - return { - name, - version, - autoImport, - }; - -} - -function loadPackageFromPath(rootDir: string, isDependency: boolean): Package { - rootDir = path.resolve(rootDir); - const data = loadPackageMetadata(rootDir); - const pkg = new Package(rootDir, data.name, data.version, [], data.autoImport, isDependency); - for (const filepath of globSync(path.join(rootDir, '**/*.bolt'))) { - pkg.addSourceFile(parseSourceFile(filepath, pkg)); - } - return pkg; -} - function error(message: string) { console.error(`Error: ${message}`); } -function loadPackagesAndSourceFiles(filenames: string[], pkgResolver: PackageResolver, cwd = '.', useStd: boolean): Package[] { - cwd = path.resolve(cwd); - const anonPkg = new Package(cwd, null, null, [], false, false); - const pkgs = [ anonPkg ]; - for (const filename of filenames) { - if (fs.statSync(filename).isDirectory()) { - pkgs.push(loadPackageFromPath(filename, false)); - } else { - anonPkg.addSourceFile(parseSourceFile(filename, anonPkg)); - } +function parsePackageResolverFlags(frontend: Frontend, flags: string[]) { + for (const flag of flags) { + const [pkgName, pkgPath] = flag.split(':'); + frontend.mapPackageNameToPath(pkgName, pkgPath) } - if (useStd && pkgs.find(pkg => pkg.name === 'stdlib') === undefined) { - const resolvedPath = pkgResolver.findPackagePath('stdlib'); - if (resolvedPath === null) { - error(`Package 'stdlib' is required to build the current source set but it was not found. Use --no-std if you know what you are doing.`); - process.exit(1); - } - const stdlibPkg = loadPackageFromPath(resolvedPath, true); - assert(stdlibPkg !== null); - pkgs.push(stdlibPkg); - } - return pkgs; -} - -class PackagePathResolver { - - private packageNameToPath = new FastStringMap(); - - public findPackagePath(name: string): string | null { - if (this.packageNameToPath.has(name)) { - return this.packageNameToPath.get(name); - } - return null; - } - - public mapPackgeNameToPath(name: string, filepath: string): void { - this.packageNameToPath.set(name, filepath); - } - } yargs @@ -215,24 +57,16 @@ yargs args => { const useStd = args['std'] as boolean ?? true; const cwd = process.cwd(); - const pkgResolver = new PackagePathResolver(); - for (const pkgMapping of toArray(args.pkg as string[] | string)) { - const [pkgName, pkgPath] = pkgMapping.split(':'); - pkgResolver.mapPackgeNameToPath(pkgName, pkgPath) - } const files = toArray(args.files as string[] | string); - if (files.length === 0) { - const metadataPath = upsearchSync('Boltfile'); - if (metadataPath === null) { - error(`No source files specified on the command-line and no Boltfile found in ${cwd} or any of its parent directories.`) - process.exit(1); - } - files.push(metadataPath); - } - const pkgs = loadPackagesAndSourceFiles(files, pkgResolver, cwd, useStd); - const program = new Program(pkgs); const frontend = new Frontend(); - frontend.check(program); + parsePackageResolverFlags(frontend, toArray(args.pkg as string | string[])); + const program = frontend.loadProgramFromFileList(files, cwd, useStd); + if (program !== null) { + frontend.check(program); + } + if (frontend.diagnostics.hasErrors) { + process.exit(1); + } } ) @@ -254,25 +88,29 @@ yargs , args => { + const force = args.force as boolean; + const useStd = args['std'] as boolean ?? true; const cwd = process.cwd(); const files = toArray(args.files as string[] | string); - if (files.length === 0) { - const metadataPath = upsearchSync('Boltfile'); - if (metadataPath === null) { - error(`No source files specified on the command-line and no Boltfile found in ${cwd} or any of its parent directories.`) - process.exit(1); - } - files.push(metadataPath); - } - const pkgs = loadPackagesAndSourceFiles(files); - const program = new Program(pkgs); + const frontend = new Frontend(); - frontend.check(program); - if (frontend.diagnostics.hasErrors && !args.force) { + + parsePackageResolverFlags(frontend, toArray(args.pkg as string | string[])); + + const program = frontend.loadProgramFromFileList(files, cwd, useStd); + + if (program === null && !force) { process.exit(1); } - frontend.compile(program, args.target); + if (program !== null) { + frontend.check(program); + if (frontend.diagnostics.hasErrors && !force) { + process.exit(1); + } + frontend.compile(program, args.target); + } + }) .command( @@ -283,19 +121,42 @@ yargs yargs => yargs .string('work-dir') .describe('work-dir', 'The working directory where files will be resolved against.') - .default('work-dir', '.'), + .default('work-dir', '.') + .boolean('skip-type-checks') + .describe('skip-type-checks', 'Do not check the program for common mistakes before evaluating.') + .default('skip-type-checks', false) + .boolean('force') + .describe('force', 'Ignore as much errors as possible.') + .default('force', false) - args => { + , args => { - const sourceFiles = toArray(args.files as string | string[]).map(parseSourceFile); + const runTypeChecker = !(args["skip-type-checks"] as boolean); + const force = args.force as boolean; + const useStd = args['std'] as boolean ?? true; + const cwd = process.cwd(); + const files = toArray(args.files as string[] | string); - if (sourceFiles.length === 0) { - throw new Error(`Executing packages is not yet supported.`) + const frontend = new Frontend(); + + parsePackageResolverFlags(frontend, toArray(args.pkg as string | string[])); + + const program = frontend.loadProgramFromFileList(files, cwd, useStd); + + if (program === null && !force) { + process.exit(1); + } + + if (program !== null) { + if (runTypeChecker) { + frontend.check(program); + } + if (frontend.diagnostics.hasErrors && !force) { + process.exit(1); + } + frontend.eval(program); } - const program = new Program(sourceFiles); - const frontend = new Frontend(); - frontend.eval(program); } diff --git a/src/common.ts b/src/common.ts index b0413825a..496a647a9 100644 --- a/src/common.ts +++ b/src/common.ts @@ -12,10 +12,12 @@ import { BoltSourceFile, isSourceFile, BoltSyntax, - BoltModifiers + BoltModifiers, + ReturnStatement, + FunctionBodyElement } from "./ast"; import { BOLT_SUPPORTED_LANGUAGES } from "./constants" -import {FastStringMap, enumerate, escapeChar, assert} from "./util"; +import {FastStringMap, enumOr, escapeChar, assert} from "./util"; import {TextSpan, TextPos, TextFile} from "./text"; import {Scanner} from "./scanner"; import * as path from "path" @@ -35,46 +37,7 @@ export function getSourceFile(node: Syntax) { export function getPackage(node: Syntax) { const sourceFile = getSourceFile(node); assert(sourceFile.kind === SyntaxKind.BoltSourceFile); - return (sourceFile as BoltSourceFile).package; -} - -let nextPackageId = 1; - -export class Package { - - public id = nextPackageId++; - - private sourceFilesByPath = new FastStringMap(); - - constructor( - public rootDir: string, - public name: string | null, - public version: string | null, - sourceFiles: SourceFile[], - public isAutoImported: boolean, - public isDependency: boolean, - ) { - for (const sourceFile of sourceFiles) { - this.addSourceFile(sourceFile); - } - } - - public getAllSourceFiles(): IterableIterator { - return this.sourceFilesByPath.values(); - } - - public getMainLibrarySourceFile(): SourceFile | null { - const fullPath = path.resolve(this.rootDir, 'lib.bolt'); - if (!this.sourceFilesByPath.has(fullPath)) { - return null; - } - return this.sourceFilesByPath.get(fullPath) - } - - public addSourceFile(sourceFile: SourceFile) { - this.sourceFilesByPath.set(sourceFile.span!.file.fullPath, sourceFile); - } - + return (sourceFile as BoltSourceFile).pkg; } export function getNodeLanguage(node: Syntax): string { @@ -175,7 +138,7 @@ export function isRightAssoc(kind: OperatorKind) { export class ParseError extends Error { constructor(public actual: Syntax, public expected: SyntaxKind[]) { - super(`${actual.span!.file.origPath}:${actual.span!.start.line}:${actual.span!.start.column}: expected ${enumerate(expected.map(e => describeKind(e)))} but got ${describeKind(actual.kind)}`) + super(`${actual.span!.file.origPath}:${actual.span!.start.line}:${actual.span!.start.column}: expected ${enumOr(expected.map(e => describeKind(e)))} but got ${describeKind(actual.kind)}`); } } @@ -220,7 +183,7 @@ export class OperatorTable { } -export function getModulePathToNode(node: BoltSyntax): string[] { +export function getModulePathToNode(node: Syntax): string[] { let elements = []; while (true) { if (node.kind === SyntaxKind.BoltModule) { @@ -420,7 +383,7 @@ export function describeKind(kind: SyntaxKind): string { } } -export function *getAllReturnStatementsInFunctionBody(body: FunctionBody): IterableIterator { +export function *getAllReturnStatementsInFunctionBody(body: FunctionBodyElement[]): IterableIterator { for (const element of body) { switch (element.kind) { case SyntaxKind.BoltReturnStatement: diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 07e85c019..260e63c79 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -1,9 +1,15 @@ +import * as fs from "fs" import chalk from "chalk" -import {Syntax} from "./ast"; -import {format, MapLike, FormatArg, countDigits, mapValues, prettyPrint} from "./util"; +import { Syntax } from "./ast"; +import {format, MapLike, FormatArg, countDigits, mapValues, prettyPrint, assert} from "./util"; import { BOLT_DIAG_NUM_EXTRA_LINES } from "./constants"; +import { TextPos, TextFile, TextSpan } from "./text"; +export const E_NO_BOLTFILE_FOUND_IN_PATH_OR_PARENT_DIRS = 'No Boltfile found in {path} or any of its parent directories.' +export const E_SSCAN_ERROR = "Got an unexpected {char}" +export const E_STDLIB_NOT_FOUND = "Package 'stdlib' is required to build the current source set but it was not found. Use --no-std if you know what you are doing." +export const E_PARSE_ERROR = "Expected {expected:enum} but got {actual}" export const E_MAY_NOT_RETURN_A_VALUE = "Returning a value inside a function that does not return values." export const E_MUST_RETURN_A_VALUE = "The function must return a value on all control paths.";;;; export const E_FILE_NOT_FOUND = "A file named {filename} was not found."; @@ -41,6 +47,8 @@ export interface Diagnostic { args?: MapLike; node?: Syntax; nested?: Diagnostic[]; + position?: TextPos, + file?: TextFile, } function firstIndexOfNonEmpty(str: string) { @@ -54,6 +62,18 @@ function firstIndexOfNonEmpty(str: string) { return j } +export class DiagnosticWriter { + + constructor(private fd: number) { + + } + + public add(diagnostic: Diagnostic) { + fs.writeSync(this.fd, JSON.stringify(diagnostic) + '\n'); + } + +} + export class DiagnosticPrinter { public hasErrors = false @@ -106,9 +126,16 @@ export class DiagnosticPrinter { out += diagnostic.message + '\n'; } + let span = null; if (diagnostic.node !== undefined) { + span = diagnostic.node.span!; + } else if (diagnostic.position !== undefined) { + assert(diagnostic.file !== undefined); + span = new TextSpan(diagnostic.file!, diagnostic.position, diagnostic.position) + } + + if (span !== null) { out += '\n' - const span = diagnostic.node.span!; const content = span.file.getText(); const startLine = Math.max(0, span.start.line-1-BOLT_DIAG_NUM_EXTRA_LINES) const lines = content.split('\n') diff --git a/src/frontend.ts b/src/frontend.ts index 81dbb6a36..65fcb4e63 100644 --- a/src/frontend.ts +++ b/src/frontend.ts @@ -1,26 +1,30 @@ import * as path from "path" import * as fs from "fs-extra" -import { now } from "microtime" import { EventEmitter } from "events" +import { sync as globSync } from "glob" import { Program } from "./program" import { emitNode } from "./emitter" -import { Syntax, BoltSourceFile, SourceFile, NodeVisitor, createBoltConditionalCase } from "./ast" -import { getFileStem, MapLike } from "./util" +import { Syntax, BoltSourceFile, SourceFile, NodeVisitor, createBoltConditionalCase, setParents, kindToString } from "./ast" +import { getFileStem, MapLike, assert, FastStringMap, upsearchSync } from "./util" import { verbose, memoize } from "./util" import { Container, Newable } from "./ioc" import ExpandBoltTransform from "./transforms/expand" import CompileBoltToJSTransform from "./transforms/boltToJS" import ConstFoldTransform from "./transforms/constFold" import { TransformManager } from "./transforms/index" -import {DiagnosticPrinter} from "./diagnostics" +import {DiagnosticPrinter, E_PARSE_ERROR, E_STDLIB_NOT_FOUND, E_SSCAN_ERROR as E_SCAN_ERROR, E_NO_BOLTFILE_FOUND_IN_PATH_OR_PARENT_DIRS} from "./diagnostics" import { TypeChecker } from "./types" -import { checkServerIdentity } from "tls" import { CheckInvalidFilePaths, CheckTypeAssignments, CheckReferences } from "./checks" import { SymbolResolver, BoltSymbolResolutionStrategy } from "./resolver" import { Evaluator } from "./evaluator" -import { getNodeLanguage } from "./common" +import { getNodeLanguage, ParseError, ScanError } from "./common" +import { Package, loadPackageMetadata } from "./package" +import { TextFile } from "./text" +import { Scanner } from "./scanner" +import { Parser } from "./parser" +import { now } from "moment" const targetExtensions: MapLike = { 'JS': '.mjs', @@ -68,11 +72,17 @@ export class Frontend { public diagnostics: DiagnosticPrinter; public timing: Timing; + private packagePathOverrides = new FastStringMap(); + constructor() { this.diagnostics = new DiagnosticPrinter(); this.timing = new Timing(); } + public mapPackageNameToPath(pkgName: string, pkgPath: string): void { + this.packagePathOverrides.set(pkgName, pkgPath); + } + public check(program: Program) { const resolver = new SymbolResolver(program, new BoltSymbolResolutionStrategy); @@ -142,7 +152,7 @@ export class Frontend { } private mapToTargetFile(node: SourceFile) { - return path.join('.bolt-work', getFileStem(node.span!.file.fullPath) + getDefaultExtension(getNodeLanguage(node))); + return path.join('.bolt-work', getFileStem(node.span!.file.fullPath) + getDefaultFileExtension(getNodeLanguage(node))); } public eval(program: Program) { @@ -154,9 +164,125 @@ export class Frontend { } } + private parseSourceFile(filepath: string, pkg: Package): BoltSourceFile | null { + + const file = new TextFile(filepath); + const contents = fs.readFileSync(file.origPath, 'utf8'); + const scanner = new Scanner(file, contents) + const parser = new Parser(); + + let sourceFile; + try { + sourceFile = parser.parseSourceFile(scanner, pkg); + } catch (e) { + if (e instanceof ScanError) { + this.diagnostics.add({ + severity: 'fatal', + message: E_SCAN_ERROR, + args: { char: e.char }, + position: e.position, + file: e.file, + }); + return null; + } else if (e instanceof ParseError) { + this.diagnostics.add({ + message: E_PARSE_ERROR, + args: { actual: kindToString(e.actual.kind), expected: e.expected.map(kindToString) }, + node: e.actual, + severity: 'fatal', + }); + return null; + } else { + throw e; + } + } + + setParents(sourceFile); + + return sourceFile; + } + + public loadPackageFromPath(filepath: string, isDependency: boolean): Package { + let metadataPath; + let rootDir; + if (path.basename(filepath) === 'Boltfile') { + metadataPath = filepath + rootDir = path.dirname(filepath); + } else { + metadataPath = path.join(filepath, 'Boltfile'); + rootDir = filepath; + } + const data = loadPackageMetadata(this.diagnostics, metadataPath); + const pkg = new Package(rootDir, data.name, data.version, [], data.autoImport, isDependency); + for (const filepath of globSync(path.join(rootDir, '**/*.bolt'))) { + const sourceFile = this.parseSourceFile(filepath, pkg); + if (sourceFile !== null) { + pkg.addSourceFile(sourceFile); + } + } + return pkg; + } + + private findPackagePath(pkgName: string): string | null { + if (this.packagePathOverrides.has(pkgName)) { + return this.packagePathOverrides.get(pkgName); + } + return null; + } + + public loadProgramFromFileList(filenames: string[], cwd = '.', useStd = true): Program | null { + + cwd = path.resolve(cwd); + + if (filenames.length === 0) { + const metadataPath = upsearchSync('Boltfile'); + if (metadataPath === null) { + this.diagnostics.add({ + severity: 'fatal', + message: E_NO_BOLTFILE_FOUND_IN_PATH_OR_PARENT_DIRS, + }); + return null; + } + filenames.push(metadataPath); + } + + const anonPkg = new Package(cwd, null, null, [], false, false); + + const pkgs = [ anonPkg ]; + + for (const filename of filenames) { + + if (fs.statSync(filename).isDirectory() || path.basename(filename) === 'Boltfile') { + pkgs.push(this.loadPackageFromPath(filename, false)); + } else { + const sourceFile = this.parseSourceFile(filename, anonPkg); + if (sourceFile !== null) { + anonPkg.addSourceFile(sourceFile); + } + } + } + + if (useStd) { + if (pkgs.find(pkg => pkg.name === 'stdlib') === undefined) { + const resolvedPath = this.findPackagePath('stdlib'); + if (resolvedPath === null) { + this.diagnostics.add({ + message: E_STDLIB_NOT_FOUND, + severity: 'error', + }); + return null; + } + const stdlibPkg = this.loadPackageFromPath(resolvedPath, true); + pkgs.push(stdlibPkg); + } + + } + return new Program(pkgs); + } + } -function getDefaultExtension(target: string) { +function getDefaultFileExtension(target: string) { if (targetExtensions[target] === undefined) { throw new Error(`Could not derive an appropriate extension for target "${target}".`) } diff --git a/src/package.ts b/src/package.ts new file mode 100644 index 000000000..1030c5a99 --- /dev/null +++ b/src/package.ts @@ -0,0 +1,134 @@ + +import * as path from "path" +import * as fs from "fs" + +import yaml from "js-yaml" +import semver from "semver" + +import {DiagnosticPrinter, E_FIELD_MUST_BE_BOOLEAN, E_FIELD_NOT_PRESENT, E_FIELD_MUST_BE_STRING, E_FIELD_HAS_INVALID_VERSION_NUMBER} from "./diagnostics"; +import {hasOwnProperty, FastStringMap} from "./util"; +import {isString} from "util" +import { SourceFile } from "./ast"; + +let nextPackageId = 1; + +export class Package { + + public id = nextPackageId++; + + private sourceFilesByPath = new FastStringMap(); + + constructor( + public rootDir: string, + public name: string | null, + public version: string | null, + sourceFiles: SourceFile[], + public isAutoImported: boolean, + public isDependency: boolean, + ) { + for (const sourceFile of sourceFiles) { + this.addSourceFile(sourceFile); + } + } + + public getAllSourceFiles(): IterableIterator { + return this.sourceFilesByPath.values(); + } + + public getMainLibrarySourceFile(): SourceFile | null { + const fullPath = path.resolve(this.rootDir, 'lib.bolt'); + if (!this.sourceFilesByPath.has(fullPath)) { + return null; + } + return this.sourceFilesByPath.get(fullPath) + } + + public addSourceFile(sourceFile: SourceFile) { + this.sourceFilesByPath.set(sourceFile.span!.file.fullPath, sourceFile); + } + +} + +export function loadPackageMetadata(diagnostics: DiagnosticPrinter, rootDir: string) { + + let name = null + let version = null; + let autoImport = false; + + let hasVersionErrors = false; + let hasNameErrors = false; + + const filepath = path.join(rootDir, 'Boltfile'); + if (fs.existsSync(filepath)) { + const data = yaml.safeLoad(fs.readFileSync(filepath, 'utf8')); + if (data !== undefined) { + if (hasOwnProperty(data, 'name')) { + if (!isString(data.name)) { + diagnostics.add({ + message: E_FIELD_MUST_BE_STRING, + severity: 'error', + args: { name: 'name' }, + }); + hasNameErrors = true; + } else { + name = data.name; + } + } + if (hasOwnProperty(data, 'version')) { + if (!isString(data.version)) { + diagnostics.add({ + message: E_FIELD_MUST_BE_STRING, + args: { name: 'version' }, + severity: 'error', + }); + hasVersionErrors = true; + } else { + if (!semver.valid(data.version)) { + diagnostics.add({ + message: E_FIELD_HAS_INVALID_VERSION_NUMBER, + args: { name: 'version' }, + severity: 'error', + }); + hasVersionErrors = true; + } else { + version = data.version; + } + } + } + if (hasOwnProperty(data, 'auto-import')) { + if (typeof(data['auto-import']) !== 'boolean') { + diagnostics.add({ + message: E_FIELD_MUST_BE_BOOLEAN, + args: { name: 'auto-import' }, + severity: 'error', + }) + } else { + autoImport = data['auto-import']; + } + } + } + } + + if (name === null && !hasNameErrors) { + diagnostics.add({ + message: E_FIELD_NOT_PRESENT, + severity: 'warning', + args: { name: 'name' }, + }); + } + + if (version === null && !hasVersionErrors) { + diagnostics.add({ + message: E_FIELD_NOT_PRESENT, + severity: 'warning', + args: { name: 'version' }, + }); + } + + return { + name, + version, + autoImport, + }; + +} diff --git a/src/parser.ts b/src/parser.ts index fd411565f..9b312d5ab 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -13,7 +13,6 @@ import { BoltParameter, BoltSourceElement, createBoltQualName, - BoltQualName, BoltPattern, createBoltBindPattern, BoltImportDirective, @@ -28,7 +27,6 @@ import { createBoltImportDirective, BoltModifiers, BoltStringLiteral, - BoltImportSymbol, BoltExpressionStatement, createBoltExpressionStatement, BoltVariableDeclaration, @@ -53,7 +51,6 @@ import { BoltSourceFile, BoltFunctionBodyElement, createBoltSourceFile, - setParents, BoltMatchExpression, createBoltMatchArm, BoltMatchArm, @@ -79,6 +76,9 @@ import { createBoltMemberExpression, BoltDeclarationLike, BoltTraitOrImplElement, + BoltQualName, + BoltLoopStatement, + createBoltLoopStatement, } from "./ast" import { parseForeignLanguage } from "./foreign" @@ -90,10 +90,14 @@ import { ParseError, setOrigNodeRange, createTokenStream, - Package, } from "./common" import { Stream, uniq, assert } from "./util" +import { Scanner } from "./scanner" +import { TextSpan, TextPos } from "./text" +import {JSScanner} from "./foreign/js/scanner"; +import { Package } from "./package" + export type BoltTokenStream = Stream; export function isModifierKeyword(kind: SyntaxKind) { @@ -201,40 +205,40 @@ export class Parser { return (this as any)['parse' + kindToString(kind).substring('Bolt'.length)](tokens); } - public parseModulePath(tokens: BoltTokenStream): BoltModulePath | null { + // private parseModulePath(tokens: BoltTokenStream): BoltModulePath | null { - let firstToken = tokens.peek();; - let lastToken: Token; - let isAbsolute = false; - let elements = []; + // let firstToken = tokens.peek();; + // let lastToken: Token; + // let isAbsolute = false; + // let elements = []; - const t0 = tokens.peek(); - if (t0.kind === SyntaxKind.BoltColonColon) { - isAbsolute = true; - tokens.get(); - lastToken = t0; - } + // const t0 = tokens.peek(); + // if (t0.kind === SyntaxKind.BoltColonColon) { + // isAbsolute = true; + // tokens.get(); + // lastToken = t0; + // } - if (tokens.peek(2).kind === SyntaxKind.BoltColonColon) { - while (true) { - const t1 = tokens.get(); - assertToken(t1, SyntaxKind.BoltIdentifier); - elements.push(t1 as BoltIdentifier) - const t2 = tokens.get(); - if (tokens.peek(2).kind !== SyntaxKind.BoltColonColon) { - lastToken = t2; - break; - } - } - } + // if (tokens.peek(2).kind === SyntaxKind.BoltColonColon) { + // while (true) { + // const t1 = tokens.get(); + // assertToken(t1, SyntaxKind.BoltIdentifier); + // elements.push(t1 as BoltIdentifier) + // const t2 = tokens.get(); + // if (tokens.peek(2).kind !== SyntaxKind.BoltColonColon) { + // lastToken = t2; + // break; + // } + // } + // } - if (!isAbsolute && elements.length === 0) { - return null; - } - const result = createBoltModulePath(isAbsolute, elements); - setOrigNodeRange(result, firstToken, lastToken!); - return result; - } + // if (!isAbsolute && elements.length === 0) { + // return null; + // } + // const result = createBoltModulePath(isAbsolute, elements); + // setOrigNodeRange(result, firstToken, lastToken!); + // return result; + // } public parseQualName(tokens: BoltTokenStream): BoltQualName { @@ -838,10 +842,22 @@ export class Parser { return node; } + public parseLoopStatement(tokens: BoltTokenStream): BoltLoopStatement { + const t0 = tokens.get(); + assertToken(t0, SyntaxKind.BoltLoopKeyword); + const t1 = tokens.get(); + assertToken(t1, SyntaxKind.BoltBraced); + const innerTokens = createTokenStream(t1); + const elements = this.parseFunctionBodyElements(innerTokens); + const result = createBoltLoopStatement(elements); + setOrigNodeRange(result, t0, t1); + return result; + } + public parseStatement(tokens: BoltTokenStream): BoltStatement { - if (this.lookaheadIsMacroCall(tokens)) { - return this.parseMacroCall(tokens); - } + // if (this.lookaheadIsMacroCall(tokens)) { + // return this.parseMacroCall(tokens); + // } const t0 = tokens.peek(); if (KIND_EXPRESSION_T0.indexOf(t0.kind) !== -1) { return this.parseExpressionStatement(tokens); @@ -1196,7 +1212,7 @@ export class Parser { tokens.get(); switch (target) { case "Bolt": - body = this.parseStatements(createTokenStream(t3)); + body = this.parseFunctionBodyElement(createTokenStream(t3)); break; default: body = parseForeignLanguage(target, t3.text, t3.span!.file, t3.span!.start); @@ -1335,10 +1351,10 @@ export class Parser { // Parse all 'fn ...' and 'type ...' elements const t5 = tokens.get(); assertToken(t5, SyntaxKind.BoltBraced); - const elements = this.parseSourceElements(createTokenStream(t5)); + const elements = this.parseTraitOrImplElements(createTokenStream(t5)); // Create and return the result - const result = createBoltImplDeclaration(modifiers, typeParams, name, traitTypeExpr, elements as BoltDeclaration[]); + const result = createBoltImplDeclaration(modifiers, typeParams, name, traitTypeExpr, elements); setOrigNodeRange(result, firstToken, t5); return result; } @@ -1577,6 +1593,13 @@ export class Parser { return this.canParseExpression(tokens); } + private canParseLoopStatement(tokens: BoltTokenStream): boolean { + if (tokens.peek(1).kind !== SyntaxKind.BoltLoopKeyword) { + return false; + } + return true; + } + private canParseStatement(tokens: BoltTokenStream): boolean { const t0 = tokens.peek(); switch (t0.kind) { @@ -1689,22 +1712,3 @@ export class Parser { } } - -import { Scanner } from "./scanner" -import { TextFile, TextSpan, TextPos } from "./text" -import * as fs from "fs" -import {JSScanner} from "./foreign/js/scanner"; -import {emitNode} from "./emitter"; -import { timingSafeEqual } from "crypto"; -import { isatty } from "tty"; - -export function parseSourceFile(filepath: string, pkg: Package): BoltSourceFile { - const file = new TextFile(filepath); - const contents = fs.readFileSync(file.origPath, 'utf8'); - const scanner = new Scanner(file, contents) - const parser = new Parser(); - const sourceFile = parser.parseSourceFile(scanner, pkg); - setParents(sourceFile); - return sourceFile; -} -