// GreyScript Compiler (gsc) // This program is self-hosting. THE FIRST TIME YOU COMPILE, however, you will // need to insert an import code directive with the absolute path to Util.src // If the first file specified for building contains "// @import" then the built // binary will be importable irrespective of passed arguments. // @dep:Util.src // @import // To use this as a library, create a Globals.src and unset GSC_STANDALONE. // Your main script should FIRST depend on Globals.src and secondly on gsc. // The global object "GsCompiler" will be available to you. if not globals.hasIndex("GSC_STANDALONE") then globals.GSC_STANDALONE = true if GSC_STANDALONE and params.len == 0 then exit("Nothing to compile") __scope = function() absPath = function(path) if path[0] == "/" then return path if path == "." then return current_path if path == ".." then return get_shell.host_computer.File(current_path).parent_path return current_path + "/" + path end function Dep = {} Dep._shl = get_shell Dep._comp = Dep._shl.host_computer Dep.build = function(files, depList = null) if depList == null then depList = {} ret = self._build(files.pull, depList) for file in files ret._subDeps.push(outer.self._build(file, outer.depList)) end for errors = ret._get_errors if errors then return errors return ret end function Dep._build = function(file, depList) ret = new self ret._depList = depList ret._subDeps = [] ret._file = absPath(file) ret._depList[ret._file] = true ret._error = ret._process return ret end function Dep._get_errors = function(self) return self._error + self._subDeps.flatMap(@self._get_errors).join("") end function Dep.filterStr = function(str) return str.indexOf("// @dep:") == 0 or str.indexOf("//@dep:") == 0 end function Dep.mapDeps = function(str) return str.split("@dep:")[1].trim end function Dep.toSubDep = function(self, str) return self._build(str, self._depList) end function Dep.makeCurry = function(toCurry) self = self ret = function(obj) return toCurry(outer.self, obj) end function return @ret end function Dep._process = function() file = self._comp.File(self._file) if not file then return "Unable to open " + self._file + "\n" if file.is_binary then return "" self._subDeps = file.get_content. split(char(10)). filter(@self.filterStr). map(@self.mapDeps). map(self.makeCurry(@self.toSubDep)) return "" end function Dep._collect = function(self) return self._subDeps.flatMap(@self._collect) + [self._file] end function Dep.collect = function(self) return self._collect.unique end function Builder = {} Builder._shl = Dep._shl Builder._comp = Dep._shl.host_computer Builder.makeStr = function(str) // GreyHack is stupid, this is required. return "import" + "_code(""" + str + """)" end function Builder.init = function(files, outFile, stdout = null) ret = new self if not stdout then stdout = @print mainFile = files[0] ret._import = self._comp.File(mainFile). get_content. split(char(10)). findOrElse("// @import", ""). indexOf("// @import") == 0 ret._root = Dep.build(files) if not ret._root isa Dep then return ret._root out = absPath(outFile).split("/") ret._outDir = out[:-1].join("/") ret._outName = out[-1] f = self._comp.File(outFile) if f and f.is_folder then ret._outDir = outFile ret._outName = self._comp.File(mainFile).name.replace("\.src", "") end if ret._stdout = @stdout s = "Compiling " if ret._import then s = s + "importable " stdout s + "to: " + ret._outDir + "/" + ret._outName return ret end function Builder.compile = function(allowImport, verbose, keepTempFile) depStrs = self._root.collect if not self._comp.touch(current_path, "__gscTemp0P.src") then exit("Unable to create temp file. No permissions?") tmpFile = self._comp.File(current_path + "/__gscTemp0P.src") if verbose then depStrs.forEach(@self._stdout) tmpFile.set_content depStrs.map(@self.makeStr).join(char(10)) ret = self._shl.build(tmpFile.path, current_path, allowImport or self._import) if not keepTempFile then tmpFile.delete bin = self._comp.File(current_path + "/__gscTemp0P") if bin then bin.move(self._outDir, self._outName) return ret end function if get_custom_object.hasIndex("__Gsh") then globals.print = get_custom_object.__Gsh[0] ArgP = {} ArgP.verbose = false ArgP.keepTempFile = false ArgP.allowImport = false ArgP["-h"] = function() print "GreyScript Compiler - v0.1.0\n" print "Designed to support nested permissions. Instead of using import code." print "You should place (for example) ""// @dep:MyDep.src"" somewhere in your code.\n" print "<b>Relative paths in source code must match where you run this compiler.</b>" print "Argument format: gsc [-t/--keep-temp-file] [-v/--verbose] [-i/--importable] file.src... [OUTFILE]\n" print "-t: Keep the 0. file that is generated in the working dir when compiling." print "-v: Write the dependency chain to the terminal. This should match the temp file.\n" print "-i: Make the generated binary importable. This is also true if the first source file" print " that you pass contains ""// @import"" anywhere in the file. Other files are ignored." print "-h: Print this help info" exit "If you do not supply an output file name, it will default to the first source file's name" end function ArgP["--help"] = @ArgP["-h"] ArgP["-t"] = function() self.keepTempFile = true end function ArgP["--keep-temp-file"] = @ArgP["-t"] ArgP["-v"] = function() self.verbose = true end function ArgP["--verbose"] = @ArgP["-v"] ArgP["-i"] = function() self.allowImport = true end function ArgP["--importable"] = @ArgP["-i"] ArgP.tocwd = function(path) return current_path + "/" + path.split("/")[-1] end function ArgP.main = function() while params.len > 0 and params[0][0] == "-" arg = params.pull if self.hasIndex(arg) then self[arg]() end while src_files = function(s) return s.indexOf(".src") != null end function not_file = function(s); return not src_files(s); end function files = params.filter(@src_files) outFile = params.findOrElse(@not_file, self.tocwd(files[0].replace("\.src", ""))) b = Builder.init(files, outFile) if not b isa Builder then return print(b + "Compilation failed") err = b.compile(self.allowImport, self.verbose, self.keepTempFile) if err then print(err) end function if GSC_STANDALONE then ArgP.main() return Builder end function // end scope globals.GsCompiler = __scope