shithub: mc

ref: 6a59af339f9aa012ce018621564d6c3c5c4bdc74
dir: /mbld/syssel.myr/

View raw version
use std

use "opts"
use "types"

pkg bld =
	type syssel(@a) = struct
		file	: byte[:]
		line	: int
		targ	: byte[:]
		sysattrs	: std.htab(byte[:], bool)#
		_match	: std.htab(byte[:], int)#
		_best	: std.htab(byte[:], @a)#
	;;

	generic mksyssel	: (b : build#, f : byte[:], line : int, targ : byte[:] -> syssel(@a)#)
	generic sysseladd	: (syssel : syssel(byte[:])#, file : byte[:] -> void)
	generic sysseladdlist	: (syssel : syssel(@a)#, base : byte[:], attrs : byte[:][:], val : @a -> void)
	generic sysselfin	: (syssel : syssel(@a)# -> @a[:])
	const addsysattrs	: (sa : build#, tags : byte[:][:] -> void)
;;

generic mksyssel = {b, file, line, targ
	var syssel

	syssel = std.mk([
		.file = file,
		.line = line,
		.targ = targ,
		._match = std.mkht(std.strhash, std.streq),
		._best = std.mkht(std.strhash, std.streq),
		.sysattrs = b.sysattrs
	])
	-> syssel
}

generic sysseladd = {syssel, f
	var basename, attrs
	var attrlist

	match std.strfind(f, "+")
	| `std.Some i:
		basename = f[:i]
		match std.strrfind(f[i+1:], ".")
		| `std.Some j:	attrs = f[i+1:][:j]
		| `std.None:	std.fatal("unrecognized type for file {}\n", f)
		;;
	| `std.None:
		match std.strrfind(f, ".")
		| `std.None:	std.fatal("unrecognized type for file {}\n", f)
		| `std.Some i:
			basename = f[:i]
			attrs = ""
		;;
	;;

	attrlist = std.strsplit(attrs, "-")
	sysseladdlist(syssel, basename, attrlist, f)
	std.slfree(attrlist)
}

generic sysseladdlist = {syssel, base, attrs, val
	var nmatch, curbest

	nmatch = 0
	for a in attrs
		if std.hthas(syssel.sysattrs, a)
			nmatch++
		else
			nmatch = -1
			break
		;;
	;;
	curbest = std.htgetv(syssel._match, base, -1)
	if curbest < nmatch
		std.htput(syssel._match, base, nmatch)
		std.htput(syssel._best, base, val)
	;;
}

generic sysselfin = {syssel
	var keys, nmatch, ret

	keys = std.htkeys(syssel._match)
	ret = [][:]
	for k in keys
		nmatch = std.htgetv(syssel._match, k, -1)
		if nmatch == -1
			std.fatal("{}:{}: target {}, no applicable file for '{}'\n", syssel.file, syssel.line, syssel.targ, k)
		;;
		std.slpush(&ret, std.get(std.htget(syssel._best, k)))
	;;
	std.htfree(syssel._match)
	std.htfree(syssel._best)
	-> ret
}

const addsysattrs = {b, tags
	var tagfile

	match opt_sys
	| "freebsd":	tag(b.sysattrs, ["freebsd", "posixy"][:])
	| "openbsd":	tag(b.sysattrs, ["openbsd", "posixy"][:])
	| "osx":	tag(b.sysattrs, ["osx", "posixy"][:])
	| "linux":	tag(b.sysattrs, ["linux", "posixy"][:])
	| "plan9":	tag(b.sysattrs, ["plan9"][:])
	| unknown:	std.fatal("unknown systemx \"{}\"\n", unknown)
	;;

	match opt_arch
	| "x64":	tag(b.sysattrs, ["x64"][:])
	| unknown:	std.fatal("unknown architecture {}\n", unknown)
	;;
	tag(b.sysattrs, tags)

	tagfile = std.pathcat(b.basedir, "systags")
	if std.fexists(tagfile)
		loadtagfile(b, tagfile)
	;;
	std.slfree(tagfile)
}

const loadtagfile = {b, tagfile
	var data, tag

	data = std.try(std.slurp(tagfile))
	while true
		data = skipspace(data)
		(tag, data) = word(data) 
		match tag
		| `std.Some w:
			std.htput(b.sysattrs, w, true)
		| `std.None:
			if data.len > 0
				std.fatal("junk character near '{}'\n", trailing(data, 10))
			else
				break
			;;
		;;
	;;
}

const skipspace = {data
	var c

	c = std.decode(data)
	while std.isspace(c)
		data = data[std.charlen(c):]
	;;
	-> data
}

const word = {data
	var c, split

	split = 0
	c = std.decode(data[:])
	while std.isalnum(c) || c == '.' || c == '_' || c == '$'
		split += std.charlen(c)
	;;
	if split > 0
		-> (`std.Some data[:split], data[split:])
	else
		-> (`std.None, data)
	;;
}

const tag  = {sa, tags
	for t in tags
		std.htput(sa, t, true)
	;;
}

const trailing = {str, len
	-> str[:std.min(len, str.len)]
}