File: context.ha

package info (click to toggle)
hare-update 0.25.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 516 kB
  • sloc: makefile: 31; sh: 14
file content (121 lines) | stat: -rw-r--r-- 2,719 bytes parent folder | download
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;
};