sys

A set of unix utils in hare!
Log | Files | Refs | README

determine.ha (8974B)


      1 // DFA transition table
      2 type dfa_transition = struct {
      3 	trans_states: []*dfa_transition_state,
      4 };
      5 
      6 // DFA transition state (row)
      7 type dfa_transition_state = struct {
      8 	states: []*state,
      9 	transitions: []*dfa_transition_trans,
     10 };
     11 
     12 // DFA transition transition (col)
     13 type dfa_transition_trans = struct {
     14 	from: *dfa_transition_state,
     15 	to: *dfa_transition_state,
     16 	input: dfa_input,
     17 };
     18 
     19 type dfa_input = (dfa_accept | dfa_negate | dfa_dot);
     20 type dfa_accept = rune;
     21 type dfa_negate = []rune;
     22 type dfa_dot = void; // only when gathering inputs, then use negate empty
     23 
     24 fn dfa_transition_finish(dfa: *dfa_transition) void = {
     25 	for (let state .. dfa.trans_states) {
     26 		dfa_transition_state_finish(state);
     27 		free(state);
     28 	};
     29 	free(dfa.trans_states);
     30 };
     31 
     32 fn dfa_transition_state_finish(dfa_state: *dfa_transition_state) void = {
     33 	for (let trans .. dfa_state.transitions) {
     34 		dfa_transition_trans_finish(trans);
     35 		free(trans);
     36 	};
     37 	free(dfa_state.transitions);
     38 	free(dfa_state.states);
     39 };
     40 
     41 fn dfa_transition_trans_finish(dfa_trans: *dfa_transition_trans) void = {
     42 	if (dfa_trans.input is dfa_negate)
     43 		free(dfa_trans.input as dfa_negate);
     44 };
     45 
     46 // Build a DFA from an NDFA. The input automata can then be finished.
     47 export fn determine(mata: *automata) (automata | nomem) = {
     48 	let dfa = dfa_transition { ... };
     49 	defer dfa_transition_finish(&dfa);
     50 
     51 	fill_dfa_transition(&dfa, mata.start)?;
     52 
     53 	const new = automata {
     54 		curindex = 0z,
     55 		curtree = 1z,
     56 		start = null: *state,
     57 		deterministic = true,
     58 		...
     59 	};
     60 	append(new.results, mata.results...)?;
     61 
     62 	fill_states(&new, &dfa)?;
     63 	new.start = new.states[0];
     64 
     65 	return new;
     66 };
     67 
     68 // Build a dfa transition table from a group of state.
     69 fn fill_dfa_transition(
     70 	dfa: *dfa_transition,
     71 	states: *state...
     72 ) (*dfa_transition_state | nomem) = {
     73 	let states = crawl_elipsis(states...)?;
     74 	defer free(states);
     75 
     76 	match (get_state(dfa, states)) {
     77 	case null => void;
     78 	case let out: *dfa_transition_state => return out;
     79 	};
     80 
     81 	const dfa_state = alloc(dfa_transition_state {
     82 		...
     83 	})?;
     84 	append(dfa_state.states, states...)?;
     85 	append(dfa.trans_states, dfa_state)?;
     86 
     87 	const all_trans = gather_trans(states)?;
     88 	defer free(all_trans);
     89 
     90 	const inputs = trans_inputs(all_trans)?;
     91 	defer free(inputs);
     92 
     93 	const groups = group_trans_input(all_trans, inputs)?;
     94 	defer {
     95 		for (let group .. groups) {
     96 			free(group.1);
     97 		};
     98 		free(groups);
     99 	};
    100 
    101 	for (let group .. groups) {
    102 		const next_states: []*state = [];
    103 		defer free(next_states);
    104 		for (let trans .. group.1) {
    105 			append(next_states, trans.to)?;
    106 		};
    107 		const next = fill_dfa_transition(dfa, next_states...)?;
    108 		const new = alloc(dfa_transition_trans {
    109 			from = dfa_state,
    110 			to = next,
    111 			input = group.0,
    112 		})?;
    113 		append(dfa_state.transitions, new)?;
    114 	};
    115 
    116 	return dfa_state;
    117 };
    118 
    119 // Gather all reachable states through elipsises.
    120 fn crawl_elipsis(locs: *state...) ([]*state | nomem) = {
    121 	let out: []*state = [];
    122 	for: root (let loc .. locs) {
    123 		for (let out .. out) if (out == loc) continue: root;
    124 		append(out, loc)?;
    125 		for: trans (let trans .. loc.transitions) {
    126 			if (0 != len(trans.accepts)) continue;
    127 			for (let out .. out) if (out == trans.to) continue: trans;
    128 			let new = crawl_elipsis(trans.to)?;
    129 			defer free(new);
    130 			for: new (let new .. new)
    131 				for (let out .. out) if (out == new) continue: new;
    132 			append(out, new...)?;
    133 		};
    134 	};
    135 	return out;
    136 };
    137 
    138 // Return the dfa transition state for a group of state when already builded.
    139 fn get_state(
    140 	dfa: *dfa_transition,
    141 	exp_states: []*state,
    142 ) nullable *dfa_transition_state = {
    143 	for (let trans_state .. dfa.trans_states) {
    144 		if (len(trans_state.states) != len(exp_states))
    145 			continue;
    146 		let matched = 0z;
    147 		for : root (let state .. trans_state.states) {
    148 			for (let _state .. exp_states) {
    149 				if (state == _state) {
    150 					matched += 1;
    151 					continue : root;
    152 				};
    153 			};
    154 		};
    155 		if (matched == len(exp_states)) return trans_state;
    156 	};
    157 	return null;
    158 };
    159 
    160 // All trans from a group of state that are not elipsises.
    161 fn gather_trans(states: []*state) ([]*transition | nomem) = {
    162 	let trans: []*transition = [];
    163 	for (let state .. states) {
    164 		for (let _trans .. state.transitions) {
    165 			if (len(_trans.accepts) == 0) {
    166 				continue;
    167 			};
    168 			append(trans, _trans)?;
    169 		};
    170 	};
    171 	return trans;
    172 };
    173 
    174 // Gather all inputs from a group of trans.
    175 fn trans_inputs(trans: []*transition) ([]dfa_input | nomem) = {
    176 	let out: []dfa_input = [];
    177 
    178 	for (let trans .. trans) {
    179 		for: accept (let accept .. trans.accepts) {
    180 			match (accept) {
    181 			case dot =>
    182 				append(out, dfa_dot)?;
    183 				continue;
    184 			case let r: rune =>
    185 				// check already appended
    186 				for (let out .. out) {
    187 					match (out) {
    188 					case let out: dfa_accept =>
    189 						if (out == r)
    190 							continue: accept;
    191 					case => void;
    192 					};
    193 				};
    194 				append(out, r)?;
    195 			case let r: (rune, rune) =>
    196 				const a = r.0: u32;
    197 				const b = r.1: u32;
    198 				const min = if (a < b) a else b;
    199 				const max = if (a < b) b else a;
    200 				for: range (let i = min; i <= max; i += 1) {
    201 					let r = i: rune;
    202 					// check already appended
    203 					for (let out .. out) {
    204 						match (out) {
    205 						case let out: dfa_accept =>
    206 							if (out == r)
    207 								continue: range;
    208 						case => void;
    209 						};
    210 					};
    211 					append(out, r)?;
    212 				};
    213 			};
    214 		};
    215 	};
    216 
    217 	return out;
    218 };
    219 
    220 // Group all accepted transitions per input.
    221 fn group_trans_input(
    222 	trans: []*transition,
    223 	inputs: []dfa_input
    224 ) ([](dfa_input, []*transition) | nomem) = {
    225 	let out: [](dfa_input, []*transition) = [];
    226 
    227 	let neg: dfa_negate = [];
    228 	defer free(neg);
    229 
    230 	let work: []*transition = [];
    231 	defer free(work);
    232 
    233 	for (let input .. inputs) {
    234 		match (input) {
    235 		case dfa_dot => void;
    236 		case dfa_negate => abort("unreachable");
    237 		case let input: dfa_accept =>
    238 			for (let trans .. trans) {
    239 				if (accept(trans, input)) {
    240 					append(work, trans)?;
    241 				};
    242 			};
    243 			append(neg, input)?;
    244 		};
    245 		if (len(work) != 0) {
    246 			append(out, (input, []))?;
    247 			let new = &out[len(out) - 1];
    248 			append(new.1, work...)?;
    249 			delete(work[..]);
    250 		};
    251 	};
    252 	for: trans (let trans .. trans) {
    253 		for (let a .. trans.accepts) {
    254 			if (!(a is dot)) continue;
    255 			append(work, trans)?;
    256 			continue: trans;
    257 		};
    258 		if (!(trans.negate)) continue;
    259 		let allfound = true;
    260 		for (let a .. trans.accepts) {
    261 			let found = false;
    262 			match (a) {
    263 			case let a: rune =>
    264 				for (let input .. neg) {
    265 					if (input == a) {
    266 						found = true;
    267 						break;
    268 					};
    269 				};
    270 			case => found = false;
    271 			};
    272 			if (!found) {
    273 				allfound = false;
    274 				break;
    275 			};
    276 		};
    277 		if (allfound) {
    278 			append(work, trans)?;
    279 		};
    280 	};
    281 	if (len(work) != 0) {
    282 		let newneg: dfa_negate = [];
    283 		append(newneg, neg...)?;
    284 		append(out, (newneg, []))?;
    285 		let new = &out[len(out) - 1];
    286 		append(new.1, work...)?;
    287 	};
    288 
    289 	return out;
    290 };
    291 
    292 // Build states from a dfa transition table.
    293 fn fill_states(
    294 	mata: *automata,
    295 	dfa: *dfa_transition,
    296 ) (void | nomem) = {
    297 	// Produce a hashmap (dfa_state -> state).
    298 	let dfa2mata: [](*dfa_transition_state, *state) = [];
    299 	defer free(dfa2mata);
    300 	for (let dfa_state .. dfa.trans_states) {
    301 		const state = alloc(state {
    302 			index = mata.curindex,
    303 			result = void,
    304 			...
    305 		})?;
    306 		mata.curindex += 1;
    307 		for (let _state .. dfa_state.states) {
    308 			if (_state.result is size) {
    309 				state.result = _state.result;
    310 				break;
    311 			};
    312 		};
    313 		append(dfa2mata, (dfa_state, state))?;
    314 		append(mata.states, state)?;
    315 	};
    316 
    317 	// Add all states linearly
    318 	for (let i = 0z; i < len(dfa.trans_states); i += 1) {
    319 		const dfa_state = dfa.trans_states[i];
    320 		const from = dfa2mata[i].1;
    321 		for: trans (let dfa_trans .. dfa_state.transitions) {
    322 			let to: nullable *state = null;
    323 			for (let dfa2mata .. dfa2mata) {
    324 				if (dfa2mata.0 == dfa_trans.to) {
    325 					to = dfa2mata.1;
    326 					break;
    327 				};
    328 			};
    329 			let to = to as *state;
    330 
    331 			// Append accepts to existing trans.
    332 			for (let trans .. from.transitions) {
    333 				if (trans.to != to) continue;
    334 				match (dfa_trans.input) {
    335 				case let acc: dfa_accept =>
    336 					if (trans.negate) continue;
    337 					append_to_accepts(&trans.accepts, acc)?;
    338 				case let neg: dfa_negate =>
    339 					if (!trans.negate) continue;
    340 					for (let neg .. neg) {
    341 						append_to_accepts(&trans.accepts, neg)?;
    342 					};
    343 				};
    344 				continue: trans;
    345 			};
    346 
    347 			// Or create a new trans.
    348 			const trans = alloc(transition {
    349 				from = from,
    350 				to = to,
    351 				tree = 0,
    352 				...
    353 			})?;
    354 			match (dfa_trans.input) {
    355 			case let acc: dfa_accept =>
    356 				append(trans.accepts, acc)?;
    357 			case let neg: dfa_negate =>
    358 				trans.negate = true;
    359 				for (let neg .. neg)
    360 					append(trans.accepts, neg)?;
    361 			};
    362 			append(mata.transitions, trans)?;
    363 			append(from.transitions, trans)?;
    364 		};
    365 	};
    366 };
    367 
    368 fn append_to_accepts(accepts: *[]runeset, r: rune) (void | nomem) = {
    369 	assert(len(accepts) != 0);
    370 	match (&accepts[len(accepts) - 1]) {
    371 	case let lastr: *rune =>
    372 		if (*lastr: u32 == r: u32 - 1) {
    373 			const lastr = *lastr;
    374 			delete(accepts[len(accepts) - 1]);
    375 			append(accepts, (lastr, r))?;
    376 			return;
    377 		};
    378 	case let lastrange: *(rune, rune) =>
    379 		if (lastrange.1: u32 == r: u32 - 1) {
    380 			lastrange.1 = r;
    381 			return;
    382 		};
    383 	};
    384 	append(accepts, r)?;
    385 };