From b3342f09281c409e7809c3f44895a76e63c45fb5 Mon Sep 17 00:00:00 2001 From: Marcus Date: Thu, 20 May 2021 00:19:25 -0400 Subject: [PATCH] lots of shit, okay? --- Instance.js | 109 --------------- package.json | 9 +- src/Instance.js | 109 +++++++++++++++ src/Module.js | 183 +++++++++++++++++++++++++ Module.js => src/Module.ts | 65 +++++---- Serializable.js => src/Serializable.js | 0 src/System.js | 99 +++++++++++++ System.js => src/System.ts | 37 +++-- src/createAst.js | 75 ++++++++++ createAst.js => src/createAst.ts | 63 ++++++++- extensions.js => src/extensions.js | 0 grammar.ne => src/grammar.ne | 10 +- src/minify.js | 14 ++ minify.js => src/minify.ts | 2 +- src/run.js | 96 +++++++++++++ run.js => src/run.ts | 23 ++-- src/tokens.js | 27 ++++ tokens.js => src/tokens.ts | 0 test/pawn.v | 6 + test/places/forest.v | 11 -- test/places/population.v | 1 - test/world.v | 26 +--- tsconfig.json | 11 ++ yarn.lock | 30 ++++ 24 files changed, 800 insertions(+), 206 deletions(-) delete mode 100644 Instance.js create mode 100644 src/Instance.js create mode 100644 src/Module.js rename Module.js => src/Module.ts (61%) rename Serializable.js => src/Serializable.js (100%) create mode 100644 src/System.js rename System.js => src/System.ts (74%) create mode 100644 src/createAst.js rename createAst.js => src/createAst.ts (68%) rename extensions.js => src/extensions.js (100%) rename grammar.ne => src/grammar.ne (84%) create mode 100644 src/minify.js rename minify.js => src/minify.ts (81%) create mode 100644 src/run.js rename run.js => src/run.ts (74%) mode change 100755 => 100644 create mode 100644 src/tokens.js rename tokens.js => src/tokens.ts (100%) create mode 100644 test/pawn.v delete mode 100644 test/places/forest.v delete mode 100644 test/places/population.v create mode 100644 tsconfig.json diff --git a/Instance.js b/Instance.js deleted file mode 100644 index 8b9e282..0000000 --- a/Instance.js +++ /dev/null @@ -1,109 +0,0 @@ - -import Serializable from './Serializable.js'; -import minify from './minify.js'; -import debug from 'debug'; -import _ from 'lodash'; -const log = debug('vogue:instance'); - - -export default class Instance extends Serializable { - module = null; - links = {} - system = null; - context = null; - locals = []; - internalFunctions = {}; - - // reconstruct context when we need it... - createContext() { - let ctx = {}; - for(const name in this.links) { - ctx[name] = this.links[name]; - } - for(const name in this.module.imports) { - ctx[name] = this.module.imports[name]; - this.locals.push(name); - } - ctx = { - ...ctx, - ...this.system.staticInstances, - ...this.internalFunctions - } - for(const identifier in this.system.staticInstances) { - this.locals.push(identifier); - } - ctx.create = this.system.newInstance.bind(this.system); - this.locals.push('create'); - this.context = ctx; - }; - - ready = false; - - constructor(module, location, parameters, system) { - super(); - this.module = module; - this.location = location; - this.system = system; - for(const name of this.module.links.optional.arrays) this.links[name] = []; - for(const name of this.module.links.optional.single) this.links[name] = null; - - for(const fnName in this.module.functions) { - this.internalFunctions[fnName] = - this.invokeInternal.bind(this, fnName); - } - this.createContext(); - - this._link = new Proxy(this, { - get(target, prop, receiver) { - if(prop === 'restore') return undefined; - if(prop in target.module.functions) { - return target.invokeInternal.bind(target, prop); - } - return undefined; - } - }); - } - - hasPublicFunction(name) { - return (name in this.module.functions); - } - - invokeInternal(name, ...args) { - log('invoking', this.module.name.full + '.' + name, 'with args', args); - const content = this.module.functions[name].code; - const passingArguments = _.zipObject(this.module.functions[name].parameters, args); - if(!content) throw new TypeError(name + ' is not a function!'); - return evalInContext(content, this.context, this.locals, passingArguments); - } - - get link () { - return this._link; - } -} - -function evalInContext(js, context, locals, passingArguments) { - //# Return the results of the in-line anonymous function we .call with the passed context - // log('='.repeat(80) + 'OG Block'); - // log(js); - // log('='.repeat(80) + 'Arguments'); - // log(passingArguments); - const that = this; - return function() { - const preminJs = -`'use strict'; -(() => { - ${locals.map((k) => `const ${k} = this.${k};`).join('\n\t')} - ${Object.keys(passingArguments).map(name => `let ${name} = passingArguments.${name};`).join('\n\t')} - ${js} -})();`; - // log('='.repeat(80) + 'preminjs'); - // log(preminJs); - const newJs = minify(preminJs); - // log('='.repeat(80) + 'minjs'); - // log(newJs); - // newJs should inject into result... - let result; - eval(newJs); - return result; - }.call(context); -} \ No newline at end of file diff --git a/package.json b/package.json index 4f9dd4f..f07eb7a 100644 --- a/package.json +++ b/package.json @@ -11,15 +11,22 @@ "c": "nearleyc", "test": "node run.js test", "debug": "cross-env DEBUG=vogue:* yarn test", - "postinstall": "cd test && yarn" + "postinstall": "cd test && yarn", + "tsc": "tsc" }, "dependencies": { + "@types/debug": "^4.1.5", + "@types/lodash": "^4.14.169", + "@types/nearley": "^2.11.1", + "@types/node": "^15.3.0", + "@types/uglify-js": "^3.13.0", "cross-env": "^7.0.3", "debug": "^4.3.1", "lodash": "^4.17.21", "moo": "^0.5.1", "nearley": "^2.20.1", "nedb": "^1.8.0", + "typescript": "^4.2.4", "uglify-js": "^3.13.5", "yarn": "^1.22.10" } diff --git a/src/Instance.js b/src/Instance.js new file mode 100644 index 0000000..21a3d23 --- /dev/null +++ b/src/Instance.js @@ -0,0 +1,109 @@ + +import Serializable from './Serializable.js'; +import minify from './minify.js'; +import debug from 'debug'; +import _ from 'lodash'; +const log = debug('vogue:instance'); +import vm from 'vm'; +/** + * @typedef {import('./System.js').default} System + * @typedef {import('./Module.js').default} Module + */ + + +export default class Instance extends Serializable { + /** @type {Module} */ + module = null; + links = {} + system = null; + context = null; + locals = []; + internalFunctions = {}; + /** @type {Proxy} */ + _link = null; + + createContext() { + const initialContext = {}; + + // system globals! + // TODO turn this into its own vogue module! system.create/instance.create + // TODO request context from system... + initialContext.create = this.system.create.bind(this.system); + for(const name of this.system.staticInstances) + initialContext[name] = this.system.staticInstances[name]; + + // local links! + // optional arrays + // TODO maybe make these property accessors to allow for some automation + for(const name of this.module.links.optional.arrays) + initialContext[name] = []; + for(const name of this.module.links.optional.single) + initialContext[name] = null; + + // local functions time! + // for(const name of this.module.functions) + + + + // let ctx = vm.createContext({ + // create: this.system.newInstance.bind(this.system), + // ...this.system.staticInstances, + // ...this.internalFunctions + // }); + + + // for(const name in this.module.imports) { + // ctx[name] = this.module.imports[name]; + // this.locals.push(name); + // } + // ctx = { + // ...ctx, + + // } + // for(const identifier in this.system.staticInstances) { + // this.locals.push(identifier); + // } + // // ctx.create = + // this.locals.push('create'); + this.context = ctx; + }; + + /** + * + * @param {Module} module + * @param {string} location + * @param {Object} parameters + * @param {System} system + */ + constructor(module, location, parameters, system) { + super(); + this.module = module; + this.location = location; + this.system = system; + + this.createContext(); + + this._link = new Proxy(this, { + get(target, prop, receiver) { + if(prop === 'restore') return undefined; + if(prop in target.module.functions) { + // TODO return the fn + return + } + return undefined; + } + }); + } + + hasPublicFunction(name) { + return (name in this.module.functions); + } + + invokeInternal(name, ...args) { + log('invoking', this.module.name.full + '.' + name, 'with args', args); + } + + get link () { + return this._link; + } +} \ No newline at end of file diff --git a/src/Module.js b/src/Module.js new file mode 100644 index 0000000..924a0c1 --- /dev/null +++ b/src/Module.js @@ -0,0 +1,183 @@ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +import { createAst } from './createAst'; +import path from 'path'; +import debug from 'debug'; +import { createRequire } from 'module'; +var log = debug('vogue:module'); +var Module = /** @class */ (function () { + function Module() { + this.links = { + required: { + single: [], + arrays: [] + }, + optional: { + single: [], + arrays: [] + } + }; + this.globals = []; + this.functions = []; + this.identifiers = {}; + this.name = { + space: '', + last: '', + full: '' + }; + this.imports = {}; + this.variables = { + cold: [], + warm: [] + }; + this.directives = { + 'singleton': false, + 'keepalive': false, + 'static': '' + }; + } + Module.prototype.directive = function (_a) { + var directive = _a.directive, value = _a.value; + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_b) { + this.directives[directive] = value; + return [2 /*return*/]; + }); + }); + }; + Module.prototype.link = function (_a) { + var required = _a.required, array = _a.array, name = _a.name; + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_b) { + this.links[required ? 'required' : 'optional'][array ? 'arrays' : 'single'] + .push(name); + return [2 /*return*/]; + }); + }); + }; + Module.prototype.namespace = function (_a) { + var namespace = _a.namespace; + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_b) { + this.name.space = namespace; + this.name.full = this.name.space + '.' + this.name.last; + return [2 /*return*/]; + }); + }); + }; + Module.prototype["function"] = function (_a) { + var name = _a.name, block = _a.block, parameters = _a.parameters; + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_b) { + this.functions[name] = { + code: block, + parameters: parameters + }; + return [2 /*return*/]; + }); + }); + }; + Module.prototype["import"] = function (_a) { + var importName = _a.importName, name = _a.name; + return __awaiter(this, void 0, void 0, function () { + var nodePath, __require__, imported; + return __generator(this, function (_b) { + nodePath = path.resolve(this.rootDir, 'node_module'); + log('#'.repeat(80)); + log(nodePath); + __require__ = createRequire(nodePath); + imported = __require__(importName); + if ('default' in imported) + this.imports[name] = imported["default"]; + else + this.imports[name] = imported; + return [2 /*return*/]; + }); + }); + }; + Module.prototype.variable = function (_a) { + var persist = _a.persist, name = _a.name; + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_b) { + this.variables[persist ? 'cold' : 'warm'].push(name); + return [2 /*return*/]; + }); + }); + }; + Module.create = function (location, rootDir) { + return __awaiter(this, void 0, void 0, function () { + var module, ast, name, _i, ast_1, item; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + module = new Module(); + ast = createAst(location); + name = path.parse(location).name; + module.name.last = name; + module.name.full = name; + module.rootDir = rootDir; + _i = 0, ast_1 = ast; + _a.label = 1; + case 1: + if (!(_i < ast_1.length)) return [3 /*break*/, 4]; + item = ast_1[_i]; + if ('name' in item) { + if (item.name in module.identifiers) + throw new Error('Identifier ' + item.name + ' already declared!'); + else + module.identifiers[item.name] = item.type; + } + if (!(item.type in module)) return [3 /*break*/, 3]; + return [4 /*yield*/, module[item.type](item)]; + case 2: + _a.sent(); + _a.label = 3; + case 3: + _i++; + return [3 /*break*/, 1]; + case 4: + log('='.repeat(80)); + log(location); + log(module); + return [2 /*return*/, module]; + } + }); + }); + }; + return Module; +}()); +export default Module; diff --git a/Module.js b/src/Module.ts similarity index 61% rename from Module.js rename to src/Module.ts index ca08df7..ecb6752 100644 --- a/Module.js +++ b/src/Module.ts @@ -1,23 +1,25 @@ -import createAst from './createAst.js' +import { createAst, DirectiveRule, DirectiveValue, FunctionRule, ImportRule, LinkRule, NamespaceRule, VariableRule } from './createAst' import path from 'path'; import debug from 'debug'; import { createRequire } from 'module'; import { pathToFileURL } from 'url'; const log = debug('vogue:module'); +type Link = { + name: string, + array: boolean, + required: boolean +} + export default class Module { - links = { - required: { - single: [], - arrays: [] - }, - optional: { - single: [], - arrays: [] - } - }; + links: Link[] = []; globals = []; - functions = []; + functions: { + [name: string]: { + code: string, + parameters: string[] + } + } = {}; identifiers = {}; name = { space: '', @@ -28,35 +30,42 @@ export default class Module { variables = { cold: [], warm: [] - } - singleton = false; - keepalive = false; - 'static' = null; + }; + directives: { + [key: string]: DirectiveValue + } = { + 'singleton': false, + 'keepalive': false, + 'static': '' + }; + rootDir: string = ''; + - async directive({directive, value}) { - this[directive] = value ?? true; + async directive({directive, value}: DirectiveRule) { + this.directives[directive] = value; } - async link({required, array, name}) { - this.links - [required ? 'required' : 'optional'] - [array ? 'arrays' : 'single'] - .push(name); + async link({required, array, name}: LinkRule) { + this.links.push({ + name, + required, + array + }); } - async namespace({namespace}) { + async namespace({namespace}: NamespaceRule) { this.name.space = namespace; this.name.full = this.name.space + '.' + this.name.last; } - async function({name, block, parameters}) { + async function({name, block, parameters}: FunctionRule) { this.functions[name] = { code: block, parameters }; } - async import({importName, name}) { + async import({importName, name}: ImportRule) { const nodePath = path.resolve(this.rootDir, 'node_module'); log('#'.repeat(80)); log(nodePath); @@ -66,11 +75,11 @@ export default class Module { else this.imports[name] = imported; } - async variable({persist, name}) { + async variable({persist, name}: VariableRule) { this.variables[persist ? 'cold' : 'warm'].push(name); } - static async create(location, rootDir) { + static async create(location: string, rootDir: string) { const module = new Module(); const ast = createAst(location); const name = path.parse(location).name; diff --git a/Serializable.js b/src/Serializable.js similarity index 100% rename from Serializable.js rename to src/Serializable.js diff --git a/src/System.js b/src/System.js new file mode 100644 index 0000000..1e2d293 --- /dev/null +++ b/src/System.js @@ -0,0 +1,99 @@ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +import Instance from './Instance'; +import Serializable from './Serializable'; +import _ from 'lodash'; +import Module from './Module'; +import debug from 'debug'; +var log = debug('vogue:system'); +var get = _.get, set = _.set; +var System = /** @class */ (function (_super) { + __extends(System, _super); + function System(modules, rootDir) { + var _this = _super.call(this) || this; + _this.instances = []; + _this.namespace = {}; + _this.staticInstances = {}; + _this.modules = modules; + _this.createNamespace(); + var bootModules = _this.deriveBootModules(); + _this.createStaticInstances(); + _this.rootDir = rootDir; + log('instantiating boot modules...'); + for (var _i = 0, bootModules_1 = bootModules; _i < bootModules_1.length; _i++) { + var name_1 = bootModules_1[_i]; + log(' ' + name_1); + _this.newInstance(name_1); + } + return _this; + } + System.prototype.createStaticInstances = function () { + log('deriving static modules...'); + var staticModules = this.modules.filter(function (module) { + return !!module.static; + }).map(function (module) { + log(' ' + module.name.full); + return module; + }); + log('instantiating static modules...'); + for (var _i = 0, staticModules_1 = staticModules; _i < staticModules_1.length; _i++) { + var module_1 = staticModules_1[_i]; + log(' ' + module_1.static + ': ' + module_1.name.full); + this.staticInstances[module_1.static] = + this.newInstance(module_1.name.full, {}); + } + }; + System.prototype.deriveBootModules = function () { + log('deriving boot modules...'); + var bootModules = this.modules.filter(function (module) { + return module.singleton; + }).map(function (module) { + return module.name.full; + }); + for (var _i = 0, bootModules_2 = bootModules; _i < bootModules_2.length; _i++) { + var name_2 = bootModules_2[_i]; + log(' ' + name_2); + } + return bootModules; + }; + System.prototype.createNamespace = function () { + log('creating namespace map...'); + this.namespace = this.modules.reduce(function (acc, val) { + if (get(acc, val.name.full) instanceof Module) + throw new Error('Duplicate module "' + val.name.full + '"'); + set(acc, val.name.full, val); + log(' ' + val.name.full); + return acc; + }, {}); + }; + System.prototype.getModule = function (name) { + return get(this.namespace, name); + }; + System.prototype.createInstance = function (name, args) { + if (args === void 0) { args = {}; } + return new Instance(this.getModule(name), null, args, this); + }; + System.prototype.newInstance = function (name, args) { + if (args === void 0) { args = {}; } + var instance = this.createInstance(name, args); + var link = instance.link; + if (instance.hasPublicFunction('restore')) + instance.invokeInternal('restore'); + return link; + }; + return System; +}(Serializable)); +export default System; diff --git a/System.js b/src/System.ts similarity index 74% rename from System.js rename to src/System.ts index 7ab0862..131c190 100644 --- a/System.js +++ b/src/System.ts @@ -1,19 +1,28 @@ -import Instance from './Instance.js'; -import Serializable from './Serializable.js'; +import Instance from './Instance'; +import Serializable from './Serializable'; import _ from 'lodash'; -import Module from './Module.js'; +import Module from './Module'; import debug from 'debug'; const log = debug('vogue:system') const {get, set} = _; -export default class System extends Serializable { - instances = []; - modules = null; - namespace = null; - staticInstances = {}; +type ModuleNamespaceMap = { + [key: string]: ModuleNamespaceMap | Module +}; - constructor(modules, rootDir) { +type ModuleName = string; + +class System extends Serializable { + instances: Instance[] = []; + modules: Module[]; + namespace: ModuleNamespaceMap = {}; + staticInstances: { + [key: string]: Instance + } = {}; + rootDir: string; + + constructor(modules: Module[], rootDir: string) { super(); this.modules = modules; this.createNamespace(); @@ -70,19 +79,21 @@ export default class System extends Serializable { }, {}); } - getModule(name) { + getModule(name: ModuleName) { return get(this.namespace, name); } - createInstance(name, args = {}) { + createInstance(name: ModuleName, args = {}) { return new Instance(this.getModule(name), null, args, this); } - newInstance(name, args = {}) { + newInstance(name: ModuleName, args = {}) { const instance = this.createInstance(name, args); const link = instance.link; if(instance.hasPublicFunction('restore')) instance.invokeInternal('restore'); return link; } -} \ No newline at end of file +} + +export default System; \ No newline at end of file diff --git a/src/createAst.js b/src/createAst.js new file mode 100644 index 0000000..b1f6965 --- /dev/null +++ b/src/createAst.js @@ -0,0 +1,75 @@ +import nearley from 'nearley'; +// TODO none of these shits have typings, but its OKAY +// @ts-ignore +import compile from 'nearley/lib/compile.js'; +// @ts-ignore +import generate from 'nearley/lib/generate.js'; +// @ts-ignore +import nearleyGrammar from 'nearley/lib/nearley-language-bootstrapped.js'; +// @ts-ignore +import moo from 'moo'; +import tokens from './tokens'; +import { readFileSync } from 'fs'; +import debug from 'debug'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; +var log = debug('vogue:ast'); +var grammarFile = resolve(fileURLToPath(dirname(import.meta.url)), 'grammar.ne'); +log('grammarFile:', grammarFile); +function createParser() { + // Parse the grammar source into an AST + var grammarParser = new nearley.Parser(nearleyGrammar); + grammarParser.feed(readFileSync(grammarFile).toString()); + var grammarAst = grammarParser.results[0]; // TODO check for errors + // Compile the AST into a set of rules + var grammarInfoObject = compile(grammarAst, {}); + // Generate JavaScript code from the rules + var grammarJs = generate(grammarInfoObject, "grammar"); + var lexer = moo.compile(tokens); + // lexer.__proto__.formatError = function(token, message) { + // if (token == null) { + // // An undefined token indicates EOF + // var text = this.buffer.slice(this.index) + // var token = { + // text: text, + // offset: this.index, + // lineBreaks: text.indexOf('\n') === -1 ? 0 : 1, + // line: this.line, + // col: this.col, + // } + // } + // var start = Math.max(0, token.offset - token.col + 1) + // var eol = token.lineBreaks ? token.text.indexOf('\n') : token.text.length + // var firstLine = this.buffer.substring(start, token.offset + eol) + // message += " at line " + token.line + " col " + token.col + ":\n\n" + // message += " " + firstLine + "\n" + // message += " " + Array(token.col).join(" ") + "^" + // return message + // } + // Pretend this is a CommonJS environment to catch exports from the grammar. + var module = { exports: {} }; + eval(grammarJs); + var grammar = module.exports; + // THESE IS COMPLICATED SHITS, IDK MAN WHAT ARE TYPINGS + // @ts-ignore + return new nearley.Parser(nearley.Grammar.fromCompiled(grammar)); +} +export function createAst(location) { + var parser = createParser(); + var contents = readFileSync(location).toString(); + // parser.reportError = function(token) { + // return JSON.stringify(token, null, 2); + // var message = this.lexer.formatError(token, 'invalid syntax') + '\n'; + // message += 'Unexpected ' + (token.type ? token.type + ' token: ' : ''); + // message += + // JSON.stringify(token.value !== undefined ? token.value : token) + '\n'; + // return message; + // }; + parser.feed(contents); + parser.finish(); + var ast = parser.results[0]; + log('='.repeat(80)); + log(location); + log(ast); + return ast || []; +} diff --git a/createAst.js b/src/createAst.ts similarity index 68% rename from createAst.js rename to src/createAst.ts index 466508f..14b20eb 100644 --- a/createAst.js +++ b/src/createAst.ts @@ -1,14 +1,20 @@ import nearley from 'nearley'; +// TODO none of these shits have typings, but its OKAY +// @ts-ignore import compile from 'nearley/lib/compile.js'; +// @ts-ignore import generate from 'nearley/lib/generate.js'; +// @ts-ignore import nearleyGrammar from 'nearley/lib/nearley-language-bootstrapped.js'; +// @ts-ignore import moo from 'moo'; -import tokens from './tokens.js'; + +import tokens from './tokens'; import { readFileSync } from 'fs'; import debug from 'debug'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; -import minify from './minify.js'; +import minify from './minify'; const log = debug('vogue:ast'); const grammarFile = resolve(fileURLToPath(dirname(import.meta.url)), 'grammar.ne'); @@ -53,10 +59,61 @@ function createParser() { eval(grammarJs); const grammar = module.exports; + // THESE IS COMPLICATED SHITS, IDK MAN WHAT ARE TYPINGS + // @ts-ignore return new nearley.Parser(nearley.Grammar.fromCompiled(grammar)) } -export default function createAst(location) { + +export type FunctionRule = { + type: 'function', + name: string + block: string, + parameters: string[] +}; + +export type VariableRule = { + type: 'variable', + persist: boolean, + name: string +}; + +export type NamespaceRule = { + type: 'namesapce', + namespace: string +}; + +export type ImportRule = { + type: 'import', + name: string, + importName: string +}; + +export type LinkRule = { + type: 'link', + array: boolean, + required: boolean, + name: string +} + +export type DirectiveValue = string | boolean; + +export type DirectiveRule = { + type: 'directive', + directive: 'static' | 'singleton' | 'keepalive', + value: DirectiveValue +} + +export type Rule = FunctionRule + | VariableRule + | ImportRule + | DirectiveRule + | LinkRule + | NamespaceRule; + +export type AST = Rule[]; + +export function createAst(location: string): AST { const parser = createParser(); const contents = readFileSync(location).toString(); diff --git a/extensions.js b/src/extensions.js similarity index 100% rename from extensions.js rename to src/extensions.js diff --git a/grammar.ne b/src/grammar.ne similarity index 84% rename from grammar.ne rename to src/grammar.ne index ba7cfdb..5211d59 100644 --- a/grammar.ne +++ b/src/grammar.ne @@ -17,7 +17,7 @@ FUNCTION_DECLARATION -> _ IDENTIFIER _ PARAMETERS:? _ JS_BLOCK EOL {% ([,name,,p | _ %ASYNC __ IDENTIFIER _ PARAMETERS:? _ JS_BLOCK_ASYNC EOL {% ([,,,name,,params,,block]) => { return { type: 'function', name: name, block, parameters: params } } %} DIRECTIVE_STATEMENT -> DIRECTIVE __ IDENTIFIER EOL {% ([directive,,identifier]) => { return { type: 'directive', directive, value: identifier }} %} - | DIRECTIVE EOL {% ([directive]) => { return { type: 'directive', directive }} %} + | DIRECTIVE EOL {% ([directive]) => { return { type: 'directive', directive, value: true }} %} DIRECTIVE -> %SINGLETON {% () => 'singleton' %} | %KEEPALIVE {% () => 'keepalive' %} @@ -42,10 +42,10 @@ EOL -> _ %SEMICOLON:? STRING -> %STRING {% ([d]) => d.value.substring(1, d.value.length - 1) %} SEMICOLON -> %SEMICOLON IDENTIFIER -> %IDENTIFIER {% ([id]) => id.value %} -JS_BLOCK -> %JS_BLOCK {% ([block]) => minify(`result = (() => {${block.value.substring(2, block.value.length - 2)}})();`) %} - | %JS_BLOCK2 {% ([block]) => minify(`result = (() => {${block.value.substring(1, block.value.length - 1)}})();`) %} -JS_BLOCK_ASYNC -> %JS_BLOCK {% ([block]) => minify(`result = (async () => {${block.value.substring(2, block.value.length - 2)}})();`) %} - | %JS_BLOCK2 {% ([block]) => minify(`result = (async () => {${block.value.substring(1, block.value.length - 1)}})();`) %} +JS_BLOCK -> %JS_BLOCK {% ([block]) => (`(() => {${block.value.substring(2, block.value.length - 2)}});`) %} + | %JS_BLOCK2 {% ([block]) => (`(() => {${block.value.substring(1, block.value.length - 1)}});`) %} +JS_BLOCK_ASYNC -> %JS_BLOCK {% ([block]) => (`(async () => {${block.value.substring(2, block.value.length - 2)}});`) %} + | %JS_BLOCK2 {% ([block]) => (`(async () => {${block.value.substring(1, block.value.length - 1)}});`) %} SPREAD_OPERATOR -> %SPREAD_OPERATOR _ -> null | %SPACE {% () => undefined %} __ -> %SPACE {% () => undefined %} \ No newline at end of file diff --git a/src/minify.js b/src/minify.js new file mode 100644 index 0000000..bbaa4ce --- /dev/null +++ b/src/minify.js @@ -0,0 +1,14 @@ +import uglify from 'uglify-js'; +export default (function (code) { + return uglify.minify(code, { + compress: { + dead_code: true, + global_defs: { + DEBUG: false + } + }, + sourceMap: { + content: 'inline' + } + }).code; +}); diff --git a/minify.js b/src/minify.ts similarity index 81% rename from minify.js rename to src/minify.ts index e2ea8d8..20535d1 100644 --- a/minify.js +++ b/src/minify.ts @@ -1,7 +1,7 @@ import uglify from 'uglify-js'; -export default (code) => { +export default (code: string): string => { return uglify.minify(code, { compress: { dead_code: true, diff --git a/src/run.js b/src/run.js new file mode 100644 index 0000000..38460fc --- /dev/null +++ b/src/run.js @@ -0,0 +1,96 @@ +#!/usr/bin/env node +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from) { + for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) + to[j] = from[i]; + return to; +}; +import debug from 'debug'; +var log = debug('vogue:cli'); +var systemLocation = resolve(process.argv[2]); +import { parse, resolve, dirname } from 'path'; +import { readdirSync, lstatSync } from 'fs'; +import _ from 'lodash'; +import Module from './Module'; +import System from './System'; +import './extensions.js'; +import { fileURLToPath } from 'url'; +var get = _.get, set = _.set; +var standardLibrary = resolve(fileURLToPath(dirname(import.meta.url)), 'lib'); +(function () { return __awaiter(void 0, void 0, void 0, function () { + var ignoreDeps, files, fullpaths, modules, sys; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + ignoreDeps = function (path) { return parse(path).name !== 'node_modules'; }; + files = __spreadArray(__spreadArray([], walkdirSync(systemLocation, ignoreDeps)), walkdirSync(standardLibrary, ignoreDeps)); + fullpaths = files + .filter(function (v) { return lstatSync(v).isFile(); }) + .filter(function (v) { return parse(v).ext === '.v'; }); + log('included modules'); + log(files); + log('parsing modules...'); + return [4 /*yield*/, Promise.all(fullpaths.map(function (loc) { return Module.create(loc, systemLocation); }))]; + case 1: + modules = _a.sent(); + sys = new System(modules, systemLocation); + return [2 /*return*/]; + } + }); +}); })(); +function walkdirSync(root, filter) { + if (filter === void 0) { filter = function () { return true; }; } + log('reading', root, '...'); + var paths = readdirSync(root).map(function (v) { return resolve(root, v); }); + var _a = sift(paths.filter(filter), function (v) { return lstatSync(v).isFile(); }), files = _a[0], dirs = _a[1]; + log("files: " + files.length + " | dirs: " + dirs.length); + var rfiles = dirs.map(function (v) { return walkdirSync(v, filter); }).reduce(function (a, v) { return __spreadArray(__spreadArray([], a), v); }, []); + return __spreadArray(__spreadArray([], files), rfiles); +} +function sift(a, fn) { + var left = [], right = []; + for (var i = 0; i < a.length; i++) { + var v = a[i]; + var lr = !!fn(v, i, a); + if (lr) + left = __spreadArray(__spreadArray([], left), [v]); + else + right = __spreadArray(__spreadArray([], right), [v]); + } + return [left, right]; +} diff --git a/run.js b/src/run.ts old mode 100755 new mode 100644 similarity index 74% rename from run.js rename to src/run.ts index 8372f30..b22aef7 --- a/run.js +++ b/src/run.ts @@ -7,19 +7,19 @@ import { parse, resolve, dirname } from 'path'; import { readdirSync, lstatSync } from 'fs'; import _ from 'lodash'; -import Module from './Module.js'; -import System from './System.js'; +import Module from './Module'; +import System from './System'; import './extensions.js'; import { fileURLToPath } from 'url'; // globals inside grammar context -import minify from './minify.js'; +import minify from './minify'; const { get, set } = _; const standardLibrary = resolve(fileURLToPath(dirname(import.meta.url)), 'lib'); (async () => { // TODO simplify this line gaddam - const ignoreDeps = (path) => parse(path).name !== 'node_modules'; + const ignoreDeps = (path: string) => parse(path).name !== 'node_modules'; const files = [ ...walkdirSync(systemLocation, ignoreDeps), @@ -37,10 +37,10 @@ const standardLibrary = resolve(fileURLToPath(dirname(import.meta.url)), 'lib'); const sys = new System(modules, systemLocation); })() -function walkdirSync(root, filter = () => true) { +function walkdirSync(root: string, filter: ((path: string) => boolean) = () => true): string[] { log('reading', root, '...'); const paths = readdirSync(root).map(v => resolve(root, v)); - const [ files, dirs ] = sift(paths.filter(filter), (v) => lstatSync(v).isFile()); + const [ files, dirs ] = sift(paths.filter(filter), (v: string) => lstatSync(v).isFile()); log(`files: ${files.length} | dirs: ${dirs.length}`); const rfiles = dirs.map(v => walkdirSync(v, filter)).reduce((a, v) => [...a, ...v], []); @@ -50,15 +50,8 @@ function walkdirSync(root, filter = () => true) { ]; } -/** - * - * @param {T[]} a - * @param {(v: T, i: number, a: T[]) => string} fn - * - * @returns {Object} - */ -function sift(a, fn) { - let left = [], right = []; +function sift(a: T[], fn: (v: T, i: number, a: T[]) => boolean): [T[], T[]] { + let left: T[] = [], right: T[] = []; for(let i = 0; i < a.length; i ++) { const v = a[i] const lr = !!fn(v, i, a); diff --git a/src/tokens.js b/src/tokens.js new file mode 100644 index 0000000..2f86c26 --- /dev/null +++ b/src/tokens.js @@ -0,0 +1,27 @@ +export default { + LINK: 'link', + NAMESPACE: 'namespace', + REQUIRED: 'required', + SINGLETON: 'singleton', + KEEPALIVE: 'keepalive', + STATIC: 'static', + MEMBER: 'member', + RUNTIME: 'runtime', + IMPORT: 'import', + ASYNC: 'async', + AS: 'as', + FROM: 'from', + COMMA: ',', + STRING: /'(?:\\['\\]|[^\n'\\])*'/, + LSQBRACKET: '[', + RSQBRACKET: ']', + LPAREN: '(', + RPAREN: ')', + SPREAD_OPERATOR: '...', + DOTOP: '.', + JS_BLOCK: /\[\[[^]*?\n\]\]$/, + JS_BLOCK2: /{[^]*?\n}$/, + IDENTIFIER: /[a-zA-Z][a-zA-Z0-9]*/, + SPACE: { match: /\s+/, lineBreaks: true }, + SEMICOLON: ';' +}; diff --git a/tokens.js b/src/tokens.ts similarity index 100% rename from tokens.js rename to src/tokens.ts diff --git a/test/pawn.v b/test/pawn.v new file mode 100644 index 0000000..72a7433 --- /dev/null +++ b/test/pawn.v @@ -0,0 +1,6 @@ + + + +restore { + +} \ No newline at end of file diff --git a/test/places/forest.v b/test/places/forest.v deleted file mode 100644 index ccfc12c..0000000 --- a/test/places/forest.v +++ /dev/null @@ -1,11 +0,0 @@ -namespace places; - -required link world; -link[] roads; - -member nitrogen; - -restore { - this.nitrogen ??= Math.floor(Math.random() * 50); -} - diff --git a/test/places/population.v b/test/places/population.v deleted file mode 100644 index 8aff8b4..0000000 --- a/test/places/population.v +++ /dev/null @@ -1 +0,0 @@ -namespace places \ No newline at end of file diff --git a/test/world.v b/test/world.v index c59b7b4..2625b35 100644 --- a/test/world.v +++ b/test/world.v @@ -1,28 +1,16 @@ -link[] places; -link place; -link character; +link[] pawns; -import random from 'random-world'; +member map; +member size; restore { - if(this.places.empty) - this.createPlaces(); + for(let i = 0; i < 3; i ++) { - this.character ??= create('') + } + + this.size ??= 64; } async render() { -} - -createPlaces() { - for(let i = 0; i < 10; i ++) { - const name = random.city(); - const valid = !!name.match(/^[A-Za-z ]*$/); - if(!valid) { - i --; - continue; - } - console.log(name); - } } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..32485d6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "allowSyntheticDefaultImports": true + }, + "include": [ + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 9c2a0bd..709555e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,28 @@ # yarn lockfile v1 +"@types/debug@^4.1.5": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" + +"@types/lodash@^4.14.169": + version "4.14.169" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.169.tgz#83c217688f07a4d9ef8f28a3ebd1d318f6ff4cbb" + +"@types/nearley@^2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@types/nearley/-/nearley-2.11.1.tgz#6ac3f57c00ca28071a1774ec72d2e45750f21420" + +"@types/node@^15.3.0": + version "15.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26" + +"@types/uglify-js@^3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.0.tgz#1cad8df1fb0b143c5aba08de5712ea9d1ff71124" + dependencies: + source-map "^0.6.1" + async@0.2.10: version "0.2.10" resolved "https://registry.npmjs.org/async/-/async-0.2.10.tgz" @@ -130,6 +152,14 @@ shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +typescript@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" + uglify-js@^3.13.5: version "3.13.5" resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.5.tgz"