diff --git a/dist/bin.d.ts b/dist/bin.d.ts new file mode 100644 index 0000000..a1f7295 --- /dev/null +++ b/dist/bin.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=bin.d.ts.map \ No newline at end of file diff --git a/dist/bin.d.ts.map b/dist/bin.d.ts.map new file mode 100644 index 0000000..41522bc --- /dev/null +++ b/dist/bin.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/bin.js b/dist/bin.js index ff445e8..0cba351 100644 --- a/dist/bin.js +++ b/dist/bin.js @@ -1,10 +1,13 @@ "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; exports.__esModule = true; var commander_1 = require("commander"); -var _1 = require("./"); +var _1 = __importDefault(require("./")); var fs = require("fs"); // Compiler Version -var version = "2.0.0"; +var version = "2.1.0"; // Define terminal commands. commander_1.program.name("huffc"); commander_1.program.version(version); @@ -14,15 +17,16 @@ commander_1.program .option("--output-directory ", "The output directory", "./") .option("--bytecode", "Generate and log bytecode", false) .option("-o, output", "The output file") - .option("-p, --paste", "Paste the output to the terminal"); + .option("-p, --paste", "Paste the output to the terminal") + .option("-n, --no-linebreak", "Omit newline charater"); // Parse the terminal arguments. commander_1.program.parse(process.argv); var options = commander_1.program.opts(); var files = commander_1.program.args; -var destination = options.outputDir || "."; +var destination = options.outputDirectory || "."; // Abort the program. var abort = function (msg) { - console.error(msg || "Error occured"); + process.stderr.write("".concat(msg, "\n") || "Error occured\n"); process.exit(1); }; // Iterate the imported files. @@ -32,16 +36,20 @@ files.forEach(function (file) { abort("File extension must be .huff"); // Compile the file. var result = (0, _1["default"])({ + kind: "file", filePath: file, generateAbi: true }); // If the user has specified an output file, write the output to the file. - var outputPath = "".concat(options.outputDirectory).concat(files[0].replace(".huff", ".json")); + var outputPath = "".concat(destination).concat(files[0].replace(".huff", ".json")); if (options.output) fs.writeFileSync(outputPath, result.abi); // If the user has specified for us to log the bytecode, log it. - if (options.bytecode && !options.paste) - console.log(result.bytecode); + if (options.bytecode && !options.paste) { + process.stdout.write(result.bytecode); + if (options.linebreak) + process.stdout.write('\n'); + } if (options.paste) - console.log(result); + process.stdout.write("".concat(JSON.stringify(result), "\n")); }); diff --git a/dist/compiler/compiler.d.ts b/dist/compiler/compiler.d.ts new file mode 100644 index 0000000..57b29be --- /dev/null +++ b/dist/compiler/compiler.d.ts @@ -0,0 +1,11 @@ +import { Definitions } from "../parser/utils/types"; +/** + * Compile a macro into raw EVM bytecode. + * @param name The name of the macro. + * @param args An array of arguments passed into the macro. + * @param macros Maps all macros to their raw text and arguments. + * @param constants Maps all constants to their values. + * @param jumptables Maps all jump tables to their jump values. + */ +export declare const compileMacro: (name: string, args: string[], macros: Definitions["data"], constants: Definitions["data"], jumptables: Definitions["data"]) => any; +//# sourceMappingURL=compiler.d.ts.map \ No newline at end of file diff --git a/dist/compiler/compiler.d.ts.map b/dist/compiler/compiler.d.ts.map new file mode 100644 index 0000000..629d5a7 --- /dev/null +++ b/dist/compiler/compiler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/compiler/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,SACjB,MAAM,QACN,MAAM,EAAE,UACN,WAAW,CAAC,MAAM,CAAC,aAChB,WAAW,CAAC,MAAM,CAAC,cAClB,WAAW,CAAC,MAAM,CAAC,QAmFhC,CAAC"} \ No newline at end of file diff --git a/dist/compiler/processor.d.ts b/dist/compiler/processor.d.ts new file mode 100644 index 0000000..85f0185 --- /dev/null +++ b/dist/compiler/processor.d.ts @@ -0,0 +1,13 @@ +import { Definitions } from "../parser/utils/types"; +/** + * Process a macro, generating semi-complete EVM bytecode. + * @param name The name of the macro. + * @param bytecodeOffset The offset of the macro's bytecode. + * @param args The arguments passed into the array. + * @param macros Maps all macros to their raw text and arguments. + * @param constants Maps all constants to their values. + * @param jumptables Maps all jump tables to their jump values. + * @returns Semi-complete EVM bytecode. + */ +export declare const processMacro: (name: string, bytecodeOffset: number, args: string[], macros: Definitions["data"], constants: Definitions["data"], jumptables: Definitions["data"]) => any; +//# sourceMappingURL=processor.d.ts.map \ No newline at end of file diff --git a/dist/compiler/processor.d.ts.map b/dist/compiler/processor.d.ts.map new file mode 100644 index 0000000..56ae28d --- /dev/null +++ b/dist/compiler/processor.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../../src/compiler/processor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAA4B,MAAM,uBAAuB,CAAC;AAG9E;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,SACjB,MAAM,kBACI,MAAM,QAChB,MAAM,EAAE,UACN,WAAW,CAAC,MAAM,CAAC,aAChB,WAAW,CAAC,MAAM,CAAC,cAClB,WAAW,CAAC,MAAM,CAAC,KAC9B,GA4OF,CAAC"} \ No newline at end of file diff --git a/dist/compiler/processor.js b/dist/compiler/processor.js index 991680d..07c4e3c 100644 --- a/dist/compiler/processor.js +++ b/dist/compiler/processor.js @@ -19,9 +19,12 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { } return to.concat(ar || Array.prototype.slice.call(from)); }; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; exports.__esModule = true; exports.processMacro = void 0; -var opcodes_1 = require("../evm/opcodes"); +var opcodes_1 = __importDefault(require("../evm/opcodes")); var macros_1 = require("../parser/macros"); var types_1 = require("../parser/utils/types"); var bytes_1 = require("../utils/bytes"); diff --git a/dist/evm/opcodes.d.ts b/dist/evm/opcodes.d.ts new file mode 100644 index 0000000..7df4d3d --- /dev/null +++ b/dist/evm/opcodes.d.ts @@ -0,0 +1,147 @@ +declare const _default: { + stop: string; + add: string; + mul: string; + sub: string; + div: string; + sdiv: string; + mod: string; + smod: string; + addmod: string; + mulmod: string; + exp: string; + signextend: string; + lt: string; + gt: string; + slt: string; + sgt: string; + eq: string; + iszero: string; + and: string; + or: string; + xor: string; + not: string; + byte: string; + sha3: string; + keccak: string; + address: string; + balance: string; + origin: string; + caller: string; + callvalue: string; + calldataload: string; + calldatasize: string; + calldatacopy: string; + codesize: string; + codecopy: string; + gasprice: string; + extcodesize: string; + extcodecopy: string; + returndatasize: string; + returndatacopy: string; + blockhash: string; + coinbase: string; + timestamp: string; + number: string; + difficulty: string; + gaslimit: string; + chainid: string; + selfbalance: string; + basefee: string; + pop: string; + mload: string; + mstore: string; + mstore8: string; + sload: string; + sstore: string; + jump: string; + jumpi: string; + getpc: string; + msize: string; + gas: string; + jumpdest: string; + push1: string; + push2: string; + push3: string; + push4: string; + push5: string; + push6: string; + push7: string; + push8: string; + push9: string; + push10: string; + push11: string; + push12: string; + push13: string; + push14: string; + push15: string; + push16: string; + push17: string; + push18: string; + push19: string; + push20: string; + push21: string; + push22: string; + push23: string; + push24: string; + push25: string; + push26: string; + push27: string; + push28: string; + push29: string; + push30: string; + push31: string; + push32: string; + dup1: string; + dup2: string; + dup3: string; + dup4: string; + dup5: string; + dup6: string; + dup7: string; + dup8: string; + dup9: string; + dup10: string; + dup11: string; + dup12: string; + dup13: string; + dup14: string; + dup15: string; + dup16: string; + swap1: string; + swap2: string; + swap3: string; + swap4: string; + swap5: string; + swap6: string; + swap7: string; + swap8: string; + swap9: string; + swap10: string; + swap11: string; + swap12: string; + swap13: string; + swap14: string; + swap15: string; + swap16: string; + shl: string; + shr: string; + sar: string; + log0: string; + log1: string; + log2: string; + log3: string; + log4: string; + create: string; + call: string; + callcode: string; + return: string; + delegatecall: string; + create2: string; + staticcall: string; + revert: string; + invalid: string; + selfdestruct: string; +}; +export default _default; +//# sourceMappingURL=opcodes.d.ts.map \ No newline at end of file diff --git a/dist/evm/opcodes.d.ts.map b/dist/evm/opcodes.d.ts.map new file mode 100644 index 0000000..124c241 --- /dev/null +++ b/dist/evm/opcodes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"opcodes.d.ts","sourceRoot":"","sources":["../../src/evm/opcodes.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,wBAgJE"} \ No newline at end of file diff --git a/dist/evm/opcodes.js b/dist/evm/opcodes.js index 4ce77f9..71b1de1 100644 --- a/dist/evm/opcodes.js +++ b/dist/evm/opcodes.js @@ -49,6 +49,8 @@ exports["default"] = { difficulty: "44", gaslimit: "45", chainid: "46", + selfbalance: "47", + basefee: "48", pop: "50", mload: "51", mstore: "52", @@ -93,21 +95,6 @@ exports["default"] = { push30: "7d", push31: "7e", push32: "7f", - log0: "a0", - log1: "a1", - log2: "a2", - log3: "a3", - log4: "a4", - create: "f0", - call: "f1", - callcode: "f2", - "return": "f3", - delegatecall: "f4", - staticcall: "fa", - create2: "fb", - revert: "fd", - invalid: "fe", - selfdestruct: "ff", dup1: "80", dup2: "81", dup3: "82", @@ -143,6 +130,19 @@ exports["default"] = { shl: "1b", shr: "1c", sar: "1d", - rol: "1e", - ror: "1f" + log0: "a0", + log1: "a1", + log2: "a2", + log3: "a3", + log4: "a4", + create: "f0", + call: "f1", + callcode: "f2", + "return": "f3", + delegatecall: "f4", + create2: "f5", + staticcall: "fa", + revert: "fd", + invalid: "fe", + selfdestruct: "ff" }; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..59f3415 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,14 @@ +import { HuffCompilerArgs } from "./types"; +/** + * Compile a Huff file. + * @param filePath The path to the file. + * @param args An array containing the arguments to the macro. + * @returns The compiled bytecode. + */ +declare const compile: (args: HuffCompilerArgs) => { + bytecode: string; + runtimeBytecode: any; + abi: string; +}; +export default compile; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map new file mode 100644 index 0000000..2839fad --- /dev/null +++ b/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C;;;;;GAKG;AACH,QAAA,MAAM,OAAO,SAAU,gBAAgB;;;;CA0EtC,CAAC;AAuBF,eAAe,OAAO,CAAC"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index 4b856f3..b382148 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,9 +1,9 @@ "use strict"; exports.__esModule = true; +var abi_1 = require("@ethersproject/abi"); var bytes_1 = require("./utils/bytes"); var compiler_1 = require("./compiler/compiler"); var high_level_1 = require("./parser/high-level"); -var ethers_1 = require("ethers"); var output_1 = require("./output"); /** * Compile a Huff file. @@ -13,7 +13,7 @@ var output_1 = require("./output"); */ var compile = function (args) { // Parse the file and generate definitions. - var _a = (0, high_level_1.parseFile)(args.filePath), macros = _a.macros, constants = _a.constants, tables = _a.tables, functions = _a.functions, events = _a.events; + var _a = (0, high_level_1.parse)(args), macros = _a.macros, constants = _a.constants, tables = _a.tables, functions = _a.functions, events = _a.events; // Generate the contract ABI. var abi = args.generateAbi ? (0, output_1.generateAbi)(functions, events) : ""; // Set storage pointer constants. @@ -48,7 +48,7 @@ var compile = function (args) { pushContractSizeCode = "61".concat(contractSize); } // Compute pushX(offset to code) - if ((bootStrapCodeSize + constructorLength) < 256) { + if (bootStrapCodeSize + constructorLength < 256) { // Convert the size and offset to bytes. var contractCodeOffset = (0, bytes_1.padNBytes)((0, bytes_1.toHex)(bootStrapCodeSize + constructorLength), 1); // push1(offset to code) @@ -66,6 +66,7 @@ var compile = function (args) { var bootstrapCode = "".concat(pushContractSizeCode, "80").concat(pushContractCodeOffset, "3d393df3"); var constructorCode = "".concat(constructorBytecode).concat(bootstrapCode); var deployedBytecode = "".concat(constructorCode).concat(mainBytecode).concat(args.constructorArgs ? encodeArgs(args.constructorArgs) : ""); + console.log(deployedBytecode); // Return the bytecode. return { bytecode: deployedBytecode, runtimeBytecode: mainBytecode, abi: abi }; }; @@ -84,8 +85,7 @@ function encodeArgs(args) { values.push(arg.value); }); // Encode and array the types and values. - var abiCoder = new ethers_1.ethers.utils.AbiCoder(); - return abiCoder.encode(types, values).replace(/^(0x)/, ""); + return abi_1.defaultAbiCoder.encode(types, values).replace(/^(0x)/, ""); } // Export compiler function as default. exports["default"] = compile; diff --git a/dist/output/index.d.ts b/dist/output/index.d.ts new file mode 100644 index 0000000..b2b774c --- /dev/null +++ b/dist/output/index.d.ts @@ -0,0 +1,10 @@ +import { Definitions } from "../parser/utils/types"; +/** + * Generate a contract ABI + * @param path The path to write the ABI to. + * @param functions Function definitions map. + * @param events Event definitions map. + * @returns Contract ABI. + */ +export declare const generateAbi: (functions: Definitions["data"], events: Definitions["data"]) => string; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/output/index.d.ts.map b/dist/output/index.d.ts.map new file mode 100644 index 0000000..3a08b09 --- /dev/null +++ b/dist/output/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/output/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,cAAe,WAAW,CAAC,MAAM,CAAC,UAAU,WAAW,CAAC,MAAM,CAAC,WA+CtF,CAAC"} \ No newline at end of file diff --git a/dist/parser/high-level.d.ts b/dist/parser/high-level.d.ts new file mode 100644 index 0000000..e672cd1 --- /dev/null +++ b/dist/parser/high-level.d.ts @@ -0,0 +1,33 @@ +import { Definitions } from "./utils/types"; +import { HuffCompilerArgs } from "../types"; +/** + * Parse a file or just the content, storing the definitions of all constants, macros, and tables. + * @param filePath The path to the file to parse. + */ +export declare const parse: (args: HuffCompilerArgs) => { + macros: Definitions; + constants: Definitions; + functions: Definitions["data"]; + events: Definitions["data"]; + tables: Definitions; +}; +export declare const setStoragePointerConstants: (macrosToSearch: string[], macros: Definitions["data"], constants: Definitions) => { + [name: string]: { + args: any[]; + value: string; + data?: any; + }; +}; +/** + * Assign constants that use the builtin FREE_STORAGE_POINTER( + * @param constants Maps the name of constants to their values + * @param order The order that the constants were declared in + */ +export declare const setStoragePointers: (constants: Definitions["data"], order: string[]) => { + [name: string]: { + args: any[]; + value: string; + data?: any; + }; +}; +//# sourceMappingURL=high-level.d.ts.map \ No newline at end of file diff --git a/dist/parser/high-level.d.ts.map b/dist/parser/high-level.d.ts.map new file mode 100644 index 0000000..330eb07 --- /dev/null +++ b/dist/parser/high-level.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"high-level.d.ts","sourceRoot":"","sources":["../../src/parser/high-level.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAW5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,KAAK,SACV,gBAAgB;YAEd,WAAW;eACR,WAAW;eACX,WAAW,CAAC,MAAM,CAAC;YACtB,WAAW,CAAC,MAAM,CAAC;YACnB,WAAW;CAgNpB,CAAC;AAEF,eAAO,MAAM,0BAA0B,mBACrB,MAAM,EAAE,UAChB,WAAW,CAAC,MAAM,CAAC,aAChB,WAAW;;;;;;CAyEvB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,cAAe,WAAW,CAAC,MAAM,CAAC,SAAS,MAAM,EAAE;;;;;;CAqCjF,CAAC"} \ No newline at end of file diff --git a/dist/parser/high-level.js b/dist/parser/high-level.js index 8990751..b2482e1 100644 --- a/dist/parser/high-level.js +++ b/dist/parser/high-level.js @@ -1,23 +1,25 @@ "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; exports.__esModule = true; -exports.setStoragePointers = exports.setStoragePointerConstants = exports.parseFile = void 0; -var contents_1 = require("./utils/contents"); +exports.setStoragePointers = exports.setStoragePointerConstants = exports.parse = void 0; +var contents_1 = __importDefault(require("./utils/contents")); var regex_1 = require("./utils/regex"); var defintions_1 = require("./syntax/defintions"); var parsing_1 = require("./utils/parsing"); var tables_1 = require("./tables"); -var macros_1 = require("./macros"); +var macros_1 = __importDefault(require("./macros")); var bytes_1 = require("../utils/bytes"); /** - * Parse a file, storing the definitions of all constants, macros, and tables. + * Parse a file or just the content, storing the definitions of all constants, macros, and tables. * @param filePath The path to the file to parse. */ -var parseFile = function (filePath) { - // Get an array of file contents. - var fileContents = (0, contents_1["default"])(filePath); - // Extract information. - var contents = fileContents.contents; - var imports = fileContents.imports; +var parse = function (args) { + // Get file fileContents and paths. + var _a = args.kind == "file" + ? (0, contents_1["default"])(args.filePath, args.sources) + : { fileContents: [(0, parsing_1.removeComments)(args.content)], filePaths: [""] }, fileContents = _a.fileContents, filePaths = _a.filePaths; // Set defintion variables. var macros = { data: {}, defintions: [] }; var constants = { data: {}, defintions: [] }; @@ -25,8 +27,8 @@ var parseFile = function (filePath) { // Set output variables. var functions = {}; var events = {}; - // Parse the file contents. - contents.forEach(function (content, contentIndex) { + // Parse the file fileContents. + fileContents.forEach(function (content, contentIndex) { var input = content; while (!(0, regex_1.isEndOfData)(input)) { // Check if we are parsing a macro definition. @@ -51,10 +53,10 @@ var parseFile = function (filePath) { // Parse constant definition. var constant = input.match(defintions_1.HIGH_LEVEL.CONSTANT); var name_1 = constant[2]; - var value = constant[3].replace("0x", ""); + var value = (0, bytes_1.formatEvenBytes)(constant[3].replace("0x", "")); // Ensure that the constant name is all uppercase. if (name_1.toUpperCase() !== name_1) - throw new SyntaxError("ParserError at ".concat(imports[contentIndex], " (Line ").concat((0, parsing_1.getLineNumber)(input, content), "): Constant ").concat(name_1, " must be uppercase.")); + throw new SyntaxError("ParserError at ".concat(filePaths[contentIndex], " (Line ").concat((0, parsing_1.getLineNumber)(input, content), "): Constant ").concat(name_1, " must be uppercase.")); // Store the constant. constants.defintions.push(name_1); constants.data[name_1] = { value: value, args: [] }; @@ -90,9 +92,9 @@ var parseFile = function (filePath) { // This is the signature of the event. var name_3 = eventDef[2]; // Store the args. - var args = (0, parsing_1.parseArgs)(eventDef[3]).map(function (arg) { return arg.replace("indexed", " indexed"); }); + var args_1 = (0, parsing_1.parseArgs)(eventDef[3]).map(function (arg) { return arg.replace("indexed", " indexed"); }); // Store the event definition. - events[name_3] = { value: name_3, args: args }; + events[name_3] = { value: name_3, args: args_1 }; // Slice the input. input = input.slice(eventDef[0].length); } @@ -116,7 +118,7 @@ var parseFile = function (filePath) { var type = table[1]; // Ensure the type is valid. if (type !== "jumptable__packed") - throw new SyntaxError("ParserError at ".concat(imports[contentIndex], " (Line ").concat((0, parsing_1.getLineNumber)(input, content), "): Table ").concat(table[0], " has invalid type: ").concat(type)); + throw new SyntaxError("ParserError at ".concat(filePaths[contentIndex], " (Line ").concat((0, parsing_1.getLineNumber)(input, content), "): Table ").concat(table[0], " has invalid type: ").concat(type)); // Parse the table. var body = table[3]; var parsed = (0, tables_1.parseJumpTable)(body, true); @@ -137,7 +139,7 @@ var parseFile = function (filePath) { var type = table[1]; // Ensure the type is valid. if (type !== "jumptable") - throw new SyntaxError("ParserError at ".concat(imports[contentIndex], " (Line ").concat((0, parsing_1.getLineNumber)(input, content), "): Table ").concat(table[0], " has invalid type: ").concat(type)); + throw new SyntaxError("ParserError at ".concat(filePaths[contentIndex], " (Line ").concat((0, parsing_1.getLineNumber)(input, content), "): Table ").concat(table[0], " has invalid type: ").concat(type)); // Parse the table. var body = table[3]; var parsed = (0, tables_1.parseJumpTable)(body, false); @@ -157,14 +159,14 @@ var parseFile = function (filePath) { // Get the line number of the file. var lineNumber = content.substring(0, index).split("\n").length; // Raise error. - throw new SyntaxError("ParserError at ".concat(imports[contentIndex], "(Line ").concat(lineNumber, "): Invalid Syntax\n \n ").concat(input.slice(0, input.indexOf("\n")), "\n ^\n ")); + throw new SyntaxError("ParserError at ".concat(filePaths[contentIndex], "(Line ").concat(lineNumber, "): Invalid Syntax\n \n ").concat(input.slice(0, input.indexOf("\n")), "\n ^\n ")); } } }); // Return all values return { macros: macros, constants: constants, functions: functions, events: events, tables: tables }; }; -exports.parseFile = parseFile; +exports.parse = parse; var setStoragePointerConstants = function (macrosToSearch, macros, constants) { // Array of used storage pointer constants. var usedStoragePointerConstants = []; @@ -201,8 +203,10 @@ var setStoragePointerConstants = function (macrosToSearch, macros, constants) { // Store the macro definition. var definition = body.match(defintions_1.MACRO_CODE.MACRO_CALL); var macroName = definition[1]; - // Get the used storage pointer constants. - getUsedStoragePointerConstants(macroName, true); + if (!macroName.startsWith("__")) { + // Get the used storage pointer constants. + getUsedStoragePointerConstants(macroName, true); + } // Slice the body. body = body.slice(definition[0].length); } diff --git a/dist/parser/macros.d.ts b/dist/parser/macros.d.ts new file mode 100644 index 0000000..596c26b --- /dev/null +++ b/dist/parser/macros.d.ts @@ -0,0 +1,10 @@ +import { Definitions, Operation } from "./utils/types"; +/** + * Parse a macro definition. + * @param args The arguments passed into the macro. + */ +declare const parseMacro: (macro: string, macros: Definitions["data"], constants: Definitions["data"], jumptables: Definitions["data"]) => Operation[]; +/** Parse argument */ +export declare const parseArgument: (input: string, macros: Definitions["data"], constants: Definitions["data"]) => Definitions["data"]; +export default parseMacro; +//# sourceMappingURL=macros.d.ts.map \ No newline at end of file diff --git a/dist/parser/macros.d.ts.map b/dist/parser/macros.d.ts.map new file mode 100644 index 0000000..bb5ecf5 --- /dev/null +++ b/dist/parser/macros.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"macros.d.ts","sourceRoot":"","sources":["../../src/parser/macros.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAiB,MAAM,eAAe,CAAC;AAEtE;;;GAGG;AACH,QAAA,MAAM,UAAU,UACP,MAAM,UACL,WAAW,CAAC,MAAM,CAAC,aAChB,WAAW,CAAC,MAAM,CAAC,cAClB,WAAW,CAAC,MAAM,CAAC,KAC9B,SAAS,EA8IX,CAAC;AAEF,qBAAqB;AACrB,eAAO,MAAM,aAAa,UACjB,MAAM,UACL,WAAW,CAAC,MAAM,CAAC,aAChB,WAAW,CAAC,MAAM,CAAC,KAC7B,WAAW,CAAC,MAAM,CAuDpB,CAAC;AAEF,eAAe,UAAU,CAAC"} \ No newline at end of file diff --git a/dist/parser/macros.js b/dist/parser/macros.js index 705e734..6a81804 100644 --- a/dist/parser/macros.js +++ b/dist/parser/macros.js @@ -1,7 +1,10 @@ "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; exports.__esModule = true; exports.parseArgument = void 0; -var opcodes_1 = require("../evm/opcodes"); +var opcodes_1 = __importDefault(require("../evm/opcodes")); var bytes_1 = require("../utils/bytes"); var defintions_1 = require("./syntax/defintions"); var parsing_1 = require("./utils/parsing"); @@ -23,7 +26,7 @@ var parseMacro = function (macro, macros, constants, jumptables) { var token = void 0; // Check if we're parsing a macro call. if (input.match(defintions_1.MACRO_CODE.MACRO_CALL) && - !(input.match(defintions_1.MACRO_CODE.MACRO_CALL) ? input.match(defintions_1.MACRO_CODE.MACRO_CALL)[1] : "").startsWith("__")) { + !(input.match(defintions_1.MACRO_CODE.MACRO_CALL)[1] || "").startsWith("__")) { // Parse the macro call. token = input.match(defintions_1.MACRO_CODE.MACRO_CALL); var name_1 = token[1]; @@ -74,7 +77,7 @@ var parseMacro = function (macro, macros, constants, jumptables) { if (!jumptables[name_4]) throw new Error("Table ".concat(name_4, " is not defined")); // Get the size of the table. - var hex = (0, bytes_1.formatEvenBytes)((0, bytes_1.toHex)(jumptables[name_4].value.length)); + var hex = (0, bytes_1.formatEvenBytes)((0, bytes_1.toHex)(jumptables[name_4].args[1])); // Add the table_size call to the token list. operations.push({ type: types_1.OperationType.PUSH, value: (0, bytes_1.toHex)(95 + hex.length / 2), args: [hex] }); } @@ -133,7 +136,7 @@ var parseArgument = function (input, macros, constants) { // If the input is a hex literal: if ((0, regex_1.isLiteral)(input)) { // Get the bytes value of the operation. - var value = input.substring(2); + var value = (0, bytes_1.formatEvenBytes)(input.substring(2)); // Get the push value var push = (0, bytes_1.toHex)(95 + value.length / 2); // Return a macro map with a single macro containing a push operation. diff --git a/dist/parser/syntax/defintions.d.ts b/dist/parser/syntax/defintions.d.ts new file mode 100644 index 0000000..0d0ffdf --- /dev/null +++ b/dist/parser/syntax/defintions.d.ts @@ -0,0 +1,25 @@ +export declare const HIGH_LEVEL: { + IMPORT: RegExp; + MACRO: RegExp; + FUNCTION: RegExp; + EVENT: RegExp; + CONSTANT: RegExp; + CODE_TABLE: RegExp; + JUMP_TABLE: RegExp; + JUMP_TABLE_PACKED: RegExp; +}; +export declare const JUMP_TABLES: { + JUMPS: RegExp; +}; +export declare const MACRO_CODE: { + TOKEN: RegExp; + LITERAL_HEX: RegExp; + MACRO_CALL: RegExp; + CONSTANT_CALL: RegExp; + CODE_SIZE: RegExp; + TABLE_SIZE: RegExp; + TABLE_START: RegExp; + ARG_CALL: RegExp; + JUMP_LABEL: RegExp; +}; +//# sourceMappingURL=defintions.d.ts.map \ No newline at end of file diff --git a/dist/parser/syntax/defintions.d.ts.map b/dist/parser/syntax/defintions.d.ts.map new file mode 100644 index 0000000..50d4615 --- /dev/null +++ b/dist/parser/syntax/defintions.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"defintions.d.ts","sourceRoot":"","sources":["../../../src/parser/syntax/defintions.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU;;;;;;;;;CAuKtB,CAAC;AAGF,eAAO,MAAM,WAAW;;CAGvB,CAAC;AAGF,eAAO,MAAM,UAAU;;;;;;;;;;CAuFtB,CAAC"} \ No newline at end of file diff --git a/dist/parser/syntax/defintions.js b/dist/parser/syntax/defintions.js index 9e1b87f..2eba8c7 100644 --- a/dist/parser/syntax/defintions.js +++ b/dist/parser/syntax/defintions.js @@ -54,7 +54,7 @@ exports.HIGH_LEVEL = { * which is then turned into the function signature. * For example "example(uint, bool)" */ - "((?:[\\s\\n]*)([a-zA-Z0-9_]+)(?:\\(([a-zA-Z0-9_,\\s\\n]+)?\\)))", + "((?:[\\s\\n]*)([a-zA-Z0-9_]+)(?:\\(([a-zA-Z0-9_\\[\\],\\s\\n]+)?\\)))", /** * The function type (payable, nonpayable, view, pure). */ @@ -66,7 +66,7 @@ exports.HIGH_LEVEL = { /** * The return type of the function within parenthesis. */ - "(?:\\(([a-zA-Z0-9_,\\s\\n]+)?\\))", + "(?:\\(([a-zA-Z0-9_\\[\\],\\s\\n]+)?\\))", ]), EVENT: (0, regex_1.combineRegexElements)([ /* #define event at the start of the line */ @@ -152,7 +152,7 @@ exports.MACRO_CODE = { /* Open Parenthesis */ "\\(", /* Any alphanumeric combination */ - "([a-zA-Z0-9_\\-<>]*)", + "([a-zA-Z0-9_, \\-<>]*)", /* Closing parenthesis */ "\\)\\s*\\n*", ]), diff --git a/dist/parser/syntax/reserved.d.ts b/dist/parser/syntax/reserved.d.ts new file mode 100644 index 0000000..d4aa4c2 --- /dev/null +++ b/dist/parser/syntax/reserved.d.ts @@ -0,0 +1,2 @@ +export declare const RESERVED_KEYWORDS: string[]; +//# sourceMappingURL=reserved.d.ts.map \ No newline at end of file diff --git a/dist/parser/syntax/reserved.d.ts.map b/dist/parser/syntax/reserved.d.ts.map new file mode 100644 index 0000000..bbf2d2a --- /dev/null +++ b/dist/parser/syntax/reserved.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"reserved.d.ts","sourceRoot":"","sources":["../../../src/parser/syntax/reserved.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,UAI7B,CAAC"} \ No newline at end of file diff --git a/dist/parser/syntax/reserved.js b/dist/parser/syntax/reserved.js new file mode 100644 index 0000000..3a16ece --- /dev/null +++ b/dist/parser/syntax/reserved.js @@ -0,0 +1,8 @@ +"use strict"; +exports.__esModule = true; +exports.RESERVED_KEYWORDS = void 0; +exports.RESERVED_KEYWORDS = [ + "__codesize", + "__tablesize", + "__tablestart" +]; diff --git a/dist/parser/tables.d.ts b/dist/parser/tables.d.ts new file mode 100644 index 0000000..18f85b9 --- /dev/null +++ b/dist/parser/tables.d.ts @@ -0,0 +1,17 @@ +/** + * Parse a code table definition + * @param body The raw string representing the body of the table. + */ +export declare const parseCodeTable: (body: string) => { + table: string; + size: number; +}; +/** + * Parse a jumptable definition + * @param body The raw string representing the body of the table. + */ +export declare const parseJumpTable: (body: string, compressed?: boolean) => { + jumps: string[]; + size: number; +}; +//# sourceMappingURL=tables.d.ts.map \ No newline at end of file diff --git a/dist/parser/tables.d.ts.map b/dist/parser/tables.d.ts.map new file mode 100644 index 0000000..af7ccf4 --- /dev/null +++ b/dist/parser/tables.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/parser/tables.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,eAAO,MAAM,cAAc,SAAU,MAAM;WAAY,MAAM;UAAQ,MAAM;CAW1E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM;WAEF,MAAM,EAAE;UAAQ,MAAM;CAgBjC,CAAC"} \ No newline at end of file diff --git a/dist/parser/utils/contents.d.ts b/dist/parser/utils/contents.d.ts new file mode 100644 index 0000000..23691c7 --- /dev/null +++ b/dist/parser/utils/contents.d.ts @@ -0,0 +1,15 @@ +export declare type Sources = Record; +export declare type FileContents = { + fileContents: string[]; + filePaths: string[]; +}; +/** + * Given a file path, return a string containing the raw + * file contents of all imported files (including files imported in imported files). + * @param entryFilePath Path to the main file of the project. + * @param sources Object with file paths (relative to the root directory) mapped to their + * contents. If no sources object is provided, the files will be read from the file system. + */ +declare const getAllFileContents: (entryFilePath: string, sources?: Sources) => FileContents; +export default getAllFileContents; +//# sourceMappingURL=contents.d.ts.map \ No newline at end of file diff --git a/dist/parser/utils/contents.d.ts.map b/dist/parser/utils/contents.d.ts.map new file mode 100644 index 0000000..2c08fa6 --- /dev/null +++ b/dist/parser/utils/contents.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"contents.d.ts","sourceRoot":"","sources":["../../../src/parser/utils/contents.ts"],"names":[],"mappings":"AAOA,oBAAY,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE7C,oBAAY,YAAY,GAAG;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAA;AAwFD;;;;;;GAMG;AACH,QAAA,MAAM,kBAAkB,kBAAmB,MAAM,YAAY,OAAO,KAAG,YAsBtE,CAAC;AAEF,eAAe,kBAAkB,CAAC"} \ No newline at end of file diff --git a/dist/parser/utils/contents.js b/dist/parser/utils/contents.js index 5753707..4eec418 100644 --- a/dist/parser/utils/contents.js +++ b/dist/parser/utils/contents.js @@ -1,52 +1,13 @@ "use strict"; /* Imports */ -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; exports.__esModule = true; var defintions_1 = require("../syntax/defintions"); var parsing_1 = require("./parsing"); var files_1 = require("../../utils/files"); -/** - * Given a file path, return a string containing the raw - * file contents of all imported files (including files imported in imported files). - * @param filepath The path to the original file - */ -var getAllFileContents = function (filepath) { - // Map indicating whether the filepath has been included. - var imported = { filepath: true }; - // An array of all imported files. - var files = [filepath]; - // Function that reads a file and adds it to the contents array. - var getContents = function (filepath, imported) { - // Read the data from the file and remove all comments. - var contents = (0, parsing_1.removeComments)((0, files_1.readFile)(filepath)); - var includes = [contents]; - var imports = [filepath]; - // Read the file data from each of the imported files. - getImports(contents).forEach(function (importPath) { - // If the file has been imported, skip. - if (imported[importPath]) - return; - var _a = getContents(importPath, imported), newIncludes = _a[0], newImports = _a[1]; - // Add the file contents to the includes array. - includes = __spreadArray(__spreadArray([], newIncludes, true), includes, true); - // Add the file to the imports array. - imports = __spreadArray(__spreadArray([], files, true), newImports, true); - // Mark the file as imported. - imported[importPath] = true; - }); - return [includes, imports]; - }; - // Get the file contents. - return { contents: getContents(filepath, imported)[0], imports: files }; -}; +var path_1 = __importDefault(require("path")); /** * Given the contents of the file, return an array * of filepaths that are imported by the file. @@ -68,4 +29,82 @@ var getImports = function (contents) { } return imports; }; +/** + * Retrieve the content and paths of a given file and all its + * imports, including nested imports. + * @param filePath Path to the file to read from. + * @param imported An object indicating whether a file has already + * been imported. + * @param getFile Function to get file contents for a given path. + * @returns Object containing the contents and path of each + * file in the project. + */ +var getNestedFileContents = function (filePath, imported, getFile) { + var fileData = getFile(filePath); + if (!fileData) + throw Error("File not found: ".concat(filePath)); + var contents = (0, parsing_1.removeComments)(fileData); + var dir = path_1["default"].parse(filePath).dir; + var relativeImports = getImports(contents); + // Normalize the import paths by making them relative + // to the root directory. + var normalizedImports = relativeImports.map(function (relativeImport) { return path_1["default"].join(dir, relativeImport); }); + var fileContents = []; + var filePaths = []; + imported[filePath] = true; + for (var _i = 0, normalizedImports_1 = normalizedImports; _i < normalizedImports_1.length; _i++) { + var importPath = normalizedImports_1[_i]; + if (imported[importPath]) + continue; + var _a = getNestedFileContents(importPath, imported, getFile), importContents = _a.fileContents, importPaths = _a.filePaths; + fileContents.push.apply(fileContents, importContents); + filePaths.push.apply(filePaths, importPaths); + } + fileContents.push(contents); + filePaths.push(filePath); + return { fileContents: fileContents, filePaths: filePaths }; +}; +var normalizeFilePath = function (filePath) { + var _a = path_1["default"].parse(filePath), dir = _a.dir, base = _a.base; + return path_1["default"].join(dir, base); +}; +/** + * Normalizes the keys in a sources object to ensure the file + * reader can access them. + */ +var normalizeSourcePaths = function (sources) { + var normalizedSources = {}; + var keys = Object.keys(sources); + for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { + var key = keys_1[_i]; + var normalizedKey = normalizeFilePath(key); + normalizedSources[normalizedKey] = sources[key]; + } + return normalizedSources; +}; +/** + * Given a file path, return a string containing the raw + * file contents of all imported files (including files imported in imported files). + * @param entryFilePath Path to the main file of the project. + * @param sources Object with file paths (relative to the root directory) mapped to their + * contents. If no sources object is provided, the files will be read from the file system. + */ +var getAllFileContents = function (entryFilePath, sources) { + // Normalize the keys in the sources object + var normalizedSources = sources && normalizeSourcePaths(sources); + // Get file from provided sources or by reading from the filesystem + var getFile = function (filePath) { + if (sources) { + var content = normalizedSources[filePath]; + if (!content) { + throw Error("File ".concat(filePath, " not found in provided sources!")); + } + return content; + } + else { + return (0, files_1.readFile)(filePath); + } + }; + return getNestedFileContents(normalizeFilePath(entryFilePath), {}, getFile); +}; exports["default"] = getAllFileContents; diff --git a/dist/parser/utils/parsing.d.ts b/dist/parser/utils/parsing.d.ts new file mode 100644 index 0000000..5fd813a --- /dev/null +++ b/dist/parser/utils/parsing.d.ts @@ -0,0 +1,18 @@ +/** + * Given a string, generate a new string without inline comments + * @param data A string of data to parse + */ +export declare const removeComments: (string: string) => string; +/** + * Given a string and an index, get the line number that the index is located on + */ +export declare const getLineNumber: (str: string, org: string) => number; +/** + * Parse an arguments list and convert it to an array + */ +export declare const parseArgs: (argString: string) => string[]; +/** + * Throw errors + */ +export declare const throwErrors: (errors: string[]) => void; +//# sourceMappingURL=parsing.d.ts.map \ No newline at end of file diff --git a/dist/parser/utils/parsing.d.ts.map b/dist/parser/utils/parsing.d.ts.map new file mode 100644 index 0000000..8e903da --- /dev/null +++ b/dist/parser/utils/parsing.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"parsing.d.ts","sourceRoot":"","sources":["../../../src/parser/utils/parsing.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,eAAO,MAAM,cAAc,WAAY,MAAM,KAAG,MAqC/C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,QAAS,MAAM,OAAO,MAAM,KAAG,MAMxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,cAAe,MAAM,aAE1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,WAAY,MAAM,EAAE,SAQ3C,CAAC"} \ No newline at end of file diff --git a/dist/parser/utils/parsing.js b/dist/parser/utils/parsing.js index 8cfeec5..7326b88 100644 --- a/dist/parser/utils/parsing.js +++ b/dist/parser/utils/parsing.js @@ -66,7 +66,7 @@ exports.parseArgs = parseArgs; var throwErrors = function (errors) { if (errors.length > 0) { errors.map(function (error) { - console.log(error); + process.stderr.write("".concat(error, "\n")); }); throw new Error(""); } diff --git a/dist/parser/utils/regex.d.ts b/dist/parser/utils/regex.d.ts new file mode 100644 index 0000000..f1cedeb --- /dev/null +++ b/dist/parser/utils/regex.d.ts @@ -0,0 +1,33 @@ +export declare const comma: RegExp; +export declare const space: RegExp; +export declare const operator: RegExp; +/** + * Combine an array of regex strings into one, seperated by "\\s*\\n*" + */ +export declare const combineRegexElements: (regexElements: string[]) => RegExp; +/** + * @param input A string containing words split by commas and spaces + * @returns An array of of string, each element is one word + */ +export declare const removeSpacesAndCommas: (input: string) => string[]; +/** + * Removes all spaces and newlines from a string + */ +export declare const removeSpacesAndLines: (input: any) => any; +/** + * @returns A boolean indicating whether the input is the end of the data. + */ +export declare const isEndOfData: (data: string) => boolean; +/** + * Count the number of times an empty character appears in a string. + */ +export declare const countSpaces: (data: string) => number; +/** + * @returns A boolean indicating whether the string contains operators. + */ +export declare const containsOperators: (input: string) => boolean; +/** + * @returns A boolean indicating whether the input is a valid literal. + */ +export declare const isLiteral: (input: any) => boolean; +//# sourceMappingURL=regex.d.ts.map \ No newline at end of file diff --git a/dist/parser/utils/regex.d.ts.map b/dist/parser/utils/regex.d.ts.map new file mode 100644 index 0000000..f627f8c --- /dev/null +++ b/dist/parser/utils/regex.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"regex.d.ts","sourceRoot":"","sources":["../../../src/parser/utils/regex.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK,QAAiC,CAAC;AACpD,eAAO,MAAM,KAAK,QAA2B,CAAC;AAC9C,eAAO,MAAM,QAAQ,QAA4B,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,oBAAoB,kBAAmB,MAAM,EAAE,KAAG,MAE9D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,UAAW,MAAM,KAAG,MAAM,EAE3D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,qBAEhC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,SAAU,MAAM,KAAG,OAE1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,SAAU,MAAM,KAAG,MAQ1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,UAAW,MAAM,YAE9C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,yBAMrB,CAAC"} \ No newline at end of file diff --git a/dist/parser/utils/types.d.ts b/dist/parser/utils/types.d.ts new file mode 100644 index 0000000..1d26947 --- /dev/null +++ b/dist/parser/utils/types.d.ts @@ -0,0 +1,31 @@ +export declare type Definitions = { + data: { + [name: string]: { + args: any[]; + value: string; + data?: any; + }; + }; + defintions: string[]; +}; +export declare type Operation = { + type: OperationType; + value: string; + args: any[]; +}; +export declare enum OperationType { + OPCODE = "OPCODE", + PUSH = "PUSH", + JUMPDEST = "JUMPDEST", + PUSH_JUMP_LABEL = "PUSH_JUMP_LABEL", + MACRO_CALL = "MACRO_CALL", + CONSTANT_CALL = "CONSTANT_CALL", + ARG_CALL = "ARG_CALL", + CODESIZE = "CODESIZE", + TABLE_START_POSITION = "TABLE_START_POSITION" +} +export declare enum Context { + NONE = 0, + MACRO = 1 +} +//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/dist/parser/utils/types.d.ts.map b/dist/parser/utils/types.d.ts.map new file mode 100644 index 0000000..0b11282 --- /dev/null +++ b/dist/parser/utils/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/parser/utils/types.ts"],"names":[],"mappings":"AACA,oBAAY,WAAW,GAAG;IACxB,IAAI,EAAE;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG;YAAE,IAAI,EAAE,GAAG,EAAE,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,GAAG,CAAA;SAAE,CAAA;KAAE,CAAC;IACrE,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAGF,oBAAY,SAAS,GAAG;IACtB,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,GAAG,EAAE,CAAC;CACb,CAAC;AAGF,oBAAY,aAAa;IACvB,MAAM,WAAW;IACjB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,eAAe,oBAAoB;IAEnC,UAAU,eAAe;IACzB,aAAa,kBAAkB;IAC/B,QAAQ,aAAa;IAErB,QAAQ,aAAa;IACrB,oBAAoB,yBAAyB;CAC9C;AAGD,oBAAY,OAAO;IACjB,IAAI,IAAI;IACR,KAAK,IAAI;CACV"} \ No newline at end of file diff --git a/dist/types.d.ts b/dist/types.d.ts new file mode 100644 index 0000000..b5aa0ad --- /dev/null +++ b/dist/types.d.ts @@ -0,0 +1,22 @@ +import { Sources } from "./parser/utils/contents"; +export declare type HuffCompilerFileArgs = { + kind: "file"; + filePath: string; + sources?: Sources; + generateAbi: boolean; + constructorArgs?: { + type: string; + value: string; + }[]; +}; +export declare type HuffCompilerContentArgs = { + kind: "content"; + content: string; + generateAbi: boolean; + constructorArgs?: { + type: string; + value: string; + }[]; +}; +export declare type HuffCompilerArgs = HuffCompilerFileArgs | HuffCompilerContentArgs; +//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/dist/types.d.ts.map b/dist/types.d.ts.map new file mode 100644 index 0000000..8865324 --- /dev/null +++ b/dist/types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAGlD,oBAAY,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrD,CAAC;AAGF,oBAAY,uBAAuB,GAAG;IACpC,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrD,CAAC;AAGF,oBAAY,gBAAgB,GAAG,oBAAoB,GAAG,uBAAuB,CAAC"} \ No newline at end of file diff --git a/dist/types.js b/dist/types.js new file mode 100644 index 0000000..0e34578 --- /dev/null +++ b/dist/types.js @@ -0,0 +1,2 @@ +"use strict"; +exports.__esModule = true; diff --git a/dist/utils/bytes.d.ts b/dist/utils/bytes.d.ts new file mode 100644 index 0000000..3d5e735 --- /dev/null +++ b/dist/utils/bytes.d.ts @@ -0,0 +1,30 @@ +/** + * Convert a hex literal to a number + * @param bytes A string representing a hex literal. + */ +export declare const convertBytesToNumber: (bytes: string) => number; +/** + * Convert a number to a hex literal + */ +export declare const convertNumberToBytes: (number: number) => string; +/** + * Given an array, find the lowest missing (non-negative) number + */ +export declare const findLowest: (value: number, arr: number[]) => number; +/** + * Given two arrays, remove all elements from the first array that aren't in the second + */ +export declare const removeNonMatching: (arr1: any[], arr2: any[]) => any[]; +/** + * Format a hex literal to make its length even + */ +export declare const formatEvenBytes: (bytes: string) => string; +/** + * Convert a hex literal to a BigNumber + */ +export declare const toHex: (number: number) => string; +/** + * Pad a hex value with zeroes. + */ +export declare const padNBytes: (hex: string, numBytes: number) => string; +//# sourceMappingURL=bytes.d.ts.map \ No newline at end of file diff --git a/dist/utils/bytes.d.ts.map b/dist/utils/bytes.d.ts.map new file mode 100644 index 0000000..2abc47d --- /dev/null +++ b/dist/utils/bytes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/utils/bytes.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,UAAW,MAAM,KAAG,MAEpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,WAAY,MAAM,KAAG,MAGrD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,UAAW,MAAM,OAAO,MAAM,EAAE,KAAG,MAEzD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,SAAU,GAAG,EAAE,QAAQ,GAAG,EAAE,UAEzD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,UAAW,MAAM,WAK5C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,KAAK,WAAY,MAAM,KAAG,MAEtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,QAAS,MAAM,YAAY,MAAM,WAKtD,CAAC"} \ No newline at end of file diff --git a/dist/utils/bytes.js b/dist/utils/bytes.js index 4c05411..4453abd 100644 --- a/dist/utils/bytes.js +++ b/dist/utils/bytes.js @@ -36,7 +36,7 @@ exports.removeNonMatching = removeNonMatching; * Format a hex literal to make its length even */ var formatEvenBytes = function (bytes) { - if (Math.floor(bytes.length / 2) * 2 !== bytes.length) { + if (bytes.length % 2) { return "0".concat(bytes); } return bytes; @@ -56,10 +56,6 @@ var padNBytes = function (hex, numBytes) { if (hex.length > numBytes * 2) { throw new Error("value ".concat(hex, " has more than ").concat(numBytes, " bytes!")); } - var result = hex; - while (result.length < numBytes * 2) { - result = "0".concat(result); - } - return result; + return hex.padStart(numBytes * 2, '0'); }; exports.padNBytes = padNBytes; diff --git a/dist/utils/files.d.ts b/dist/utils/files.d.ts new file mode 100644 index 0000000..52dbb06 --- /dev/null +++ b/dist/utils/files.d.ts @@ -0,0 +1,6 @@ +/** + * Read a file and return its contents + * @param filePath The path to the file + */ +export declare const readFile: (filePath: string) => string; +//# sourceMappingURL=files.d.ts.map \ No newline at end of file diff --git a/dist/utils/files.d.ts.map b/dist/utils/files.d.ts.map new file mode 100644 index 0000000..3b28118 --- /dev/null +++ b/dist/utils/files.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/utils/files.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,eAAO,MAAM,QAAQ,aAAc,MAAM,KAAG,MAM3C,CAAC"} \ No newline at end of file diff --git a/dist/utils/files.js b/dist/utils/files.js index 9309693..2d14a19 100644 --- a/dist/utils/files.js +++ b/dist/utils/files.js @@ -8,6 +8,10 @@ var fs = require("fs"); * @param filePath The path to the file */ var readFile = function (filePath) { - return fs.readFileSync(path.posix.resolve(filePath), "utf8"); + var resolvedPath = path.resolve(filePath); + if (!fs.existsSync(filePath)) { + throw Error("File ".concat(filePath, " not found!")); + } + return fs.readFileSync(resolvedPath, "utf8"); }; exports.readFile = readFile; diff --git a/package.json b/package.json index d5ce12e..b135583 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "huffc", "description": "A low level programming language for the Ethereum Virtual Machine", - "version": "0.0.25", + "version": "0.1.25", "author": "JetJadeja", "bin": { "huffc": "bin/huffc" diff --git a/src/bin.ts b/src/bin.ts index f09a5e9..1722a8f 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -3,7 +3,7 @@ import compile from "./"; import fs = require("fs"); // Compiler Version -const version = "2.0.0"; +const version = "2.1.0"; // Define terminal commands. program.name("huffc"); @@ -22,7 +22,7 @@ program.parse(process.argv); const options = program.opts(); var files = program.args; -var destination = options.outputDir || "."; +var destination = options.outputDirectory || "."; // Abort the program. const abort = (msg) => { @@ -37,12 +37,13 @@ files.forEach((file) => { // Compile the file. const result = compile({ + kind: "file", filePath: file, generateAbi: true, }); // If the user has specified an output file, write the output to the file. - const outputPath = `${options.outputDirectory}${files[0].replace(".huff", ".json")}`; + const outputPath = `${destination}${files[0].replace(".huff", ".json")}`; if (options.output) fs.writeFileSync(outputPath, result.abi); // If the user has specified for us to log the bytecode, log it. diff --git a/src/index.ts b/src/index.ts index 01c8200..3386da3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,27 +1,18 @@ import { defaultAbiCoder } from "@ethersproject/abi"; import { padNBytes, toHex } from "./utils/bytes"; import { compileMacro } from "./compiler/compiler"; -import { parseFile, setStoragePointerConstants } from "./parser/high-level"; +import { parse, setStoragePointerConstants } from "./parser/high-level"; import { generateAbi } from "./output"; -import { Sources } from "./parser/utils/contents"; - -/* Compilation Input Type */ -type HuffCompilerArgs = { - filePath: string; - sources?: Sources; - generateAbi: boolean; - constructorArgs?: { type: string; value: string }[]; -}; +import { HuffCompilerArgs } from "./types"; /** - * Compile a Huff file. - * @param filePath The path to the file. - * @param args An array containing the arguments to the macro. + * Compile a Huff file/data. + * @param args file or data info. * @returns The compiled bytecode. */ const compile = (args: HuffCompilerArgs) => { // Parse the file and generate definitions. - const { macros, constants, tables, functions, events } = parseFile(args.filePath, args.sources); + const { macros, constants, tables, functions, events } = parse(args); // Generate the contract ABI. const abi = args.generateAbi ? generateAbi(functions, events) : ""; diff --git a/src/parser/high-level.ts b/src/parser/high-level.ts index fc5e41f..f1985fa 100644 --- a/src/parser/high-level.ts +++ b/src/parser/high-level.ts @@ -1,20 +1,25 @@ -import getAllFileContents, { Sources } from "./utils/contents"; +import getAllFileContents from "./utils/contents"; import { isEndOfData } from "./utils/regex"; import { Definitions } from "./utils/types"; import { HIGH_LEVEL, MACRO_CODE } from "./syntax/defintions"; -import { parseArgs, getLineNumber } from "./utils/parsing"; +import { parseArgs, getLineNumber, removeComments } from "./utils/parsing"; import { parseCodeTable, parseJumpTable } from "./tables"; import parseMacro from "./macros"; -import { convertBytesToNumber, convertNumberToBytes, findLowest, formatEvenBytes } from "../utils/bytes"; +import { + convertBytesToNumber, + convertNumberToBytes, + findLowest, + formatEvenBytes, +} from "../utils/bytes"; +import { HuffCompilerArgs } from "../types"; /** - * Parse a file, storing the definitions of all constants, macros, and tables. - * @param filePath The path to the file to parse. + * Parse a file or just the content, storing the definitions of all constants, macros, and tables. + * @param args file or data info. */ -export const parseFile = ( - filePath: string, - sources?: Sources +export const parse = ( + args: HuffCompilerArgs ): { macros: Definitions; constants: Definitions; @@ -23,7 +28,10 @@ export const parseFile = ( tables: Definitions; } => { // Get file fileContents and paths. - const {fileContents, filePaths} = getAllFileContents(filePath, sources); + const { fileContents, filePaths } = + args.kind == "file" + ? getAllFileContents(args.filePath, args.sources) + : { fileContents: [removeComments(args.content)], filePaths: [""] }; // Set defintion variables. const macros: Definitions = { data: {}, defintions: [] }; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..ddf840e --- /dev/null +++ b/src/types.ts @@ -0,0 +1,21 @@ +import { Sources } from "./parser/utils/contents"; + +/* Compilation File Input Type */ +export type HuffCompilerFileArgs = { + kind: "file"; + filePath: string; + sources?: Sources; + generateAbi: boolean; + constructorArgs?: { type: string; value: string }[]; +}; + +/* Compilation Content Only Input Type */ +export type HuffCompilerContentArgs = { + kind: "content"; + content: string; + generateAbi: boolean; + constructorArgs?: { type: string; value: string }[]; +}; + +/* Compilation Input Type */ +export type HuffCompilerArgs = HuffCompilerFileArgs | HuffCompilerContentArgs; diff --git a/tsconfig.json b/tsconfig.json index 8abd6b6..b318b90 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,8 @@ "compilerOptions": { "module": "commonjs", "outDir": "./dist", + "declaration": true, + "declarationMap": true, "suppressImplicitAnyIndexErrors": true, "noImplicitAny": false, "esModuleInterop": true,