Skip to content

Commit

Permalink
Some semblance of multi-file imports
Browse files Browse the repository at this point in the history
  • Loading branch information
dusty-phillips committed Aug 25, 2024
1 parent 38a9a4e commit 6b0ee5a
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 61 deletions.
38 changes: 26 additions & 12 deletions src/compiler/program.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
//// filesystem.
//// It does not write to the filesystem.

import compiler/python
import errors
import filepath
import glance
import gleam/dict
import gleam/list
import gleam/result
import pprint
import simplifile

pub type GleamProgram {
GleamProgram(modules: dict.Dict(String, glance.Module))
GleamProgram(
source_directory: String,
modules: dict.Dict(String, glance.Module),
)
}

pub type CompiledProgram {
Expand All @@ -24,9 +26,22 @@ pub type CompiledProgram {

/// Load the entry_point file and recursively load and parse any modules it
///returns.
pub fn load_program(entry_point: String) -> Result(GleamProgram, errors.Error) {
GleamProgram(modules: dict.new())
|> load_module(entry_point)
pub fn load_program(
source_directory: String,
) -> Result(GleamProgram, errors.Error) {
source_directory
|> simplifile.is_directory
|> result.map_error(errors.FileOrDirectoryNotFound(source_directory, _))
|> result.try(fn(_) { find_entrypoint(source_directory) })
|> result.try(load_module(GleamProgram(source_directory, dict.new()), _))
}

pub fn find_entrypoint(source_directory: String) -> Result(String, errors.Error) {
let base_name = filepath.base_name(source_directory)
let entrypoint = base_name <> ".gleam"
simplifile.is_file(filepath.join(source_directory, entrypoint))
|> result.replace(entrypoint)
|> result.map_error(errors.FileOrDirectoryNotFound(entrypoint, _))
}

/// Parse the module and add it to the program's modules, if it can be parsed.
Expand All @@ -35,12 +50,12 @@ fn load_module(
program: GleamProgram,
module_path: String,
) -> Result(GleamProgram, errors.Error) {
pprint.debug(module_path)
case dict.get(program.modules, module_path) {
Ok(_) -> Ok(program)
Error(_) -> {
let module_result =
module_path
|> filepath.join(program.source_directory, _)
|> simplifile.read
|> result.map_error(errors.FileReadError(module_path, _))
|> result.try(parse(_, module_path))
Expand Down Expand Up @@ -81,9 +96,8 @@ fn add_module(
module_path: String,
module_contents: glance.Module,
) -> GleamProgram {
GleamProgram(modules: dict.insert(
program.modules,
module_path,
module_contents,
))
GleamProgram(
..program,
modules: dict.insert(program.modules, module_path, module_contents),
)
}
11 changes: 9 additions & 2 deletions src/errors.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@ import internal/errors as internal
import simplifile

pub type Error {
FileReadError(module: String, error: simplifile.FileError)
FileWriteError(module: String, error: simplifile.FileError)
FileOrDirectoryNotFound(path: String, error: simplifile.FileError)
FileReadError(path: String, error: simplifile.FileError)
FileWriteError(path: String, error: simplifile.FileError)
DeleteError(path: String, error: simplifile.FileError)
MkdirError(path: String, error: simplifile.FileError)
GlanceParseError(error: glance.Error, module: String, contents: String)
}

pub fn format_error(error: Error) -> String {
case error {
FileOrDirectoryNotFound(filename, _) ->
"File or directory not found " <> filename
FileReadError(filename, simplifile.Enoent) -> "File not found " <> filename
FileReadError(filename, _) -> "Unable to read " <> filename
FileWriteError(filename, _) -> "Unable to write " <> filename
DeleteError(filename, _) -> "Unable to delete " <> filename
MkdirError(filename, _) -> "Unable to mkdir " <> filename
GlanceParseError(error, filename, contents) ->
internal.format_glance_error(error, filename, contents)
}
Expand Down
61 changes: 26 additions & 35 deletions src/macabre.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,47 @@ import argv
import compiler
import compiler/program
import errors
import filepath
import gleam/dict
import gleam/io
import gleam/result
import gleam/string
import internal/errors as error_functions
import output
import pprint
import simplifile

pub fn usage(message: String) -> Nil {
io.println("Usage: macabre <filename.gleam>\n\n" <> message)
}

fn compile_module(filename: String) -> Result(Nil, String) {
simplifile.read(filename)
|> result.replace_error("Unable to read '" <> filename <> "'")
|> result.try(fn(content) {
content
|> compiler.compile
|> result.map_error(error_functions.format_glance_error(
_,
filename,
content,
))
})
|> result.try(output.write(_, output.replace_extension(filename)))
fn write_program(
program: program.CompiledProgram,
build_directory: String,
) -> Result(Nil, errors.Error) {
build_directory
|> output.delete
|> result.try(fn(_) { output.create_directory(build_directory) })
|> result.try(fn(_) { output.write_prelude_file(build_directory) })
|> result.try(fn(_) {
// TODO: eventually, this has to be output to a base directory,
// not one copy per module.
filename
|> output.replace_file("gleam_builtins.py")
|> output.write_prelude_file
dict.fold(program.modules, Ok(Nil), fn(state, name, module) {
result.try(state, fn(_) {
build_directory
|> filepath.join(name)
|> output.replace_extension()
|> output.write(module, _)
})
})
})
}

pub fn main() {
case argv.load().arguments {
[] -> usage("Not enough arguments")
[input] ->
case string.ends_with(input, ".gleam") {
False -> usage(input <> ":" <> " Not a gleam input file")
True -> {
input
|> program.load_program
|> result.map(compiler.compile_program)
|> result.try(output.write_program(_, "build"))
|> result.map_error(output.write_error)
|> result.unwrap_both
// both nil
}
}
[directory] ->
directory
|> program.load_program
|> result.map(compiler.compile_program)
|> result.try(write_program(_, filepath.join(directory, "build/")))
|> result.map_error(output.write_error)
|> result.unwrap_both
// both nil
[_, _, ..] -> usage("Too many arguments")
}
}
27 changes: 15 additions & 12 deletions src/output.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import compiler/program
import errors
import filepath
import gleam/io
Expand All @@ -11,7 +10,7 @@ pub fn write(contents: String, filename: String) -> Result(Nil, errors.Error) {
|> result.map_error(errors.FileWriteError(filename, _))
}

fn replace_extension(filename: String) -> String {
pub fn replace_extension(filename: String) -> String {
filename |> filepath.strip_extension <> ".py"
}

Expand All @@ -33,21 +32,25 @@ pub fn compile_result(filename: String, result: Result(Nil, String)) -> Nil {
}
}

pub fn write_prelude_file(filepath: String) -> Result(Nil, String) {
filepath
|> simplifile.write(python_prelude.gleam_builtins)
|> result.replace_error("Unable to write prelude")
}
pub fn write_prelude_file(build_directory: String) -> Result(Nil, errors.Error) {
let full_path = filepath.join(build_directory, "gleam_builtins.py")

pub fn write_program(
program: program.CompiledProgram,
directory: String,
) -> Result(Nil, errors.Error) {
todo
full_path
|> simplifile.write(python_prelude.gleam_builtins)
|> result.map_error(errors.FileWriteError(full_path, _))
}

pub fn write_error(error: errors.Error) -> Nil {
error
|> errors.format_error
|> io.println
}

pub fn delete(path: String) -> Result(Nil, errors.Error) {
simplifile.delete_all([path]) |> result.map_error(errors.DeleteError(path, _))
}

pub fn create_directory(path) -> Result(Nil, errors.Error) {
simplifile.create_directory(path)
|> result.map_error(errors.MkdirError(path, _))
}

0 comments on commit 6b0ee5a

Please sign in to comment.