1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
|
use bufio;
use common;
use fmt;
use fs;
use glue;
use io;
use memio;
use os;
use unix::tty;
export type engine = struct {
ver: const *version,
tty: io::file,
ctx: (*context | void),
yes: bool,
};
// Creates a new rules engine for applying updates to Hare source code for the
// desired target Hare version. Pass the return value to [[destroy]] when you're
// done with it.
export fn new_engine(ver: const *version, yes: bool = false) *engine = {
const tty = match (tty::open()) {
case let tty: io::file =>
yield tty;
case let err: tty::error =>
fmt::fatalf("Error opening TTY: {}", tty::strerror(err));
};
let eng = alloc(engine {
ver = ver,
tty = tty,
ctx = void,
yes = yes,
})!;
const glue = ver.glue;
for (let rule .. ver.rules) {
for (let hook .. rule.hooks) {
glue.parse_register_hook(hook, rule.exec, eng);
};
};
return eng;
};
// Frees resources associated with an [[engine]].
export fn destroy(eng: *engine) void = {
io::close(eng.tty)!;
free(eng);
};
export fn getcontext(user: nullable *opaque) *context = {
const eng = user: *engine;
return eng.ctx as *context;
};
// Context for applying rules to a single source file.
export type context = struct {
engine: const *engine,
glue: const *glue::glue,
path: str,
file: io::file,
stat: fs::filestat,
buffer: *memio::stream,
newlines: []io::off,
scan: *bufio::scanner,
lex: *glue::lexer,
edits: []edit,
};
// Creates a new context for applying rules to a specific file. Pass the context
// to [[exec]] to execute the upgrade rules for this file.
export fn new_context(
eng: *engine,
path: str,
) (*context | fs::error | io::error) = {
assert(eng.ctx is void, "Cannot have two contexts active at once");
const glue = eng.ver.glue;
const file = os::open(path, fs::flag::RDWR)?;
const stat = os::fstat(file)!;
let data = io::drain(file)?;
let newlines = find_newlines(data);
const mbuf = alloc(memio::dynamic_from(data))!;
const scan = alloc(bufio::newscanner(mbuf))!;
const lex = glue.lex_init(scan, path);
const ctx = alloc(context {
engine = eng,
glue = glue,
path = path,
file = file,
stat = stat,
buffer = mbuf,
newlines = newlines,
scan = scan,
lex = lex,
edits = [],
})!;
eng.ctx = ctx;
return ctx;
};
// Executes rules against a [[context]]. This consumes the [[context]], which
// can no longer be used.
export fn exec(ctx: *context) (document | common::error) = {
defer context_destroy(ctx);
ctx.glue.parse(ctx.lex)?;
return document_init(ctx);
};
fn context_destroy(ctx: *context) void = {
let eng = ctx.engine;
ctx.glue.lex_free(ctx.lex);
bufio::finish(ctx.scan);
free(ctx.newlines);
free(ctx.buffer);
free(ctx.scan);
free(ctx);
eng.ctx = void;
};
|