shithub: mc

ref: 9b51700784b0b76155ad10591b9c224cf80cf3a6
dir: /parse.myr/

View raw version
use std

use "types.use"

pkg bld =
	const parse	: (p : parser#	-> bool)
;;

const failparse = {p : parser#, msg, args : ...
	var buf : byte[1024]
	var ap
	var n

	ap = std.vastart(&args)
	n = std.bfmtv(buf[:], msg, ap)
	std.fput(1, "%s:%i: %s", p.fname, p.line, buf[:n])
	std.exit(1)
}

const parse = {p
	while true
		skipspace(p)
		if !target(p)
			break
		;;
	;;
	skipspace(p)
	if p.rest.len > 0
		failparse(p, "junk in file near %s", p.rest[:std.min(p.rest.len, 10)])
		-> false
	else
		-> true
	;;
}

const target = {p : parser#
	match word(p)
	| `std.Some "bin":	bintarget(p)
	| `std.Some "lib":	libtarget(p)
	| `std.Some "sub":	subtarget(p)
	| `std.Some "man":	mantarget(p)
	| `std.Some targtype:	failparse(p, "unknown targtype type %s\n", targtype)
	| `std.None:	-> false
	;;
	-> true
}

const bintarget = {p
	p.targs = std.slpush(p.targs, `Bin myrtarget(p, "bin"))
}

const libtarget = {p
	p.targs = std.slpush(p.targs, `Lib myrtarget(p, "lib"))
}

const subtarget = {p
	p.targs = std.slpush(p.targs, `Sub anontarget(p, "sub"))
}

const mantarget = {p
	p.targs = std.slpush(p.targs, `Man anontarget(p, "man"))

}
const myrtarget = {p, targ
	var name, inputs, attrs
	var ldscript, runtime, inst

	match word(p)
	| `std.Some n:	name = n
	| `std.None:	failparse(p, "expected target name after '%s'\n", targ)
	;;

	skipspace(p)
	if matchc(p, '{')
		match attrlist(p)
		| `std.Some al:	attrs = al
		| `std.None:	failparse(p, "invalid attr list for %s %s", targ, name)
		;;
	else
		attrs = [][:]
	;;

	skipspace(p)
	if !matchc(p, '=')
		failparse(p, "expected '=' after '%s %s'", targ, name)
	;;

	match wordlist(p)
	| `std.Some wl: inputs = wl
	| `std.None: failparse(p, "expected list of file names after '%s %s'\n", targ, name)
	;;
	skipspace(p)
	if !matchc(p, ';')
		failparse(p, "expected ';' terminating input list, got %c\n", peekc(p))
	;;

	inst = true
	ldscript = ""
	runtime = ""
	for elt in attrs
		match elt
		| ("ldscript", lds):	ldscript = std.sldup(lds)
		| ("runtime", rt):	runtime = std.sldup(rt)
		| ("noinst", val):
			if val.len != 0
				failparse(p, "noinst attr does not take argument\n")
			;;
			inst = false
		;;
	;;
	-> [
		.name=name,
		.inputs=inputs,
		.install=inst,
		.ldscript=ldscript,
		.runtime=runtime
	]
}

const anontarget = {p, targ
	var inputs

	skipspace(p)
	if !matchc(p, '=')
		failparse(p, "expected '=' after '%s' target", targ)
	;;

	match wordlist(p)
	| `std.None:	failparse(p, "expected list of file names after '%s' target\n", targ)
	| `std.Some wl:	inputs = wl
	;;
	skipspace(p)
	if !matchc(p, ';')
		failparse(p, "expected ';' terminating input list\n")
	;;
	-> inputs
}

const attrlist = {p
	var al

	al = [][:]
	while true
		match word(p)
		| `std.Some k:
			skipspace(p)
			if matchc(p, '=')
				match word(p)
				| `std.Some v:
					al = std.slpush(al, (k, v))
				| `std.None:
					failparse(p, "invalid attr in attribute list\n")
				;;
			else
				al = std.slpush(al, (k, [][:]))
			;;
		| `std.None:	break
		;;
	;;
	if !matchc(p, '}')
		failparse(p, "expected '}' at end of attr list\n")
	;;
	if al.len == 0
		-> `std.None
	else
		-> `std.Some al
	;;
}


const wordlist = {p
	var wl

	wl = [][:]
	while true
		match word(p)
		| `std.Some w:	wl = std.slpush(wl, w)
		| `std.None:	break
		;;
	;;
	if wl.len == 0
		-> `std.None
	else
		-> `std.Some wl
	;;
}

const word = {p : parser#
	var c, r, n
	var start

	skipspace(p)

	r = p.rest
	start = r
	n = 0
	while r.len > 0
		c = peekc(p)
		if wordchar(c)
			nextc(p)
			n += std.charlen(c)
		else
			break
		;;
	;;
	if n > 0
		-> `std.Some std.sldup(start[:n])
	else
		-> `std.None
	;;
}

const wordchar = {c
	-> std.isalnum(c) || \
		c == '.' || c == '_' || c == '$' || c == '-' || \
		c == '/' || c == ':' || c == '!' || c == '~'
}

const skipspace = {p : parser#
	var c, r

	r = p.rest
	while r.len > 0
		c = peekc(p)
		match c
		| ' ':	nextc(p)
		| '\t':	nextc(p)
		| '\n':
			nextc(p)
			p.line++
		| '#':
			while p.rest.len > 0 && peekc(p) != '\n'
				nextc(p)
			;;
		| _:
			break
		;;
	;;
}

const matchc = {p, c
	var chr, s

	if p.rest.len == 0
		-> false
	;;
	(chr, s) = std.striter(p.rest)
	if c == chr
		p.rest = s
		-> true
	else
		-> false
	;;
}

const peekc = {p
	-> std.decode(p.rest)
}

const nextc = {p
	var c, s

	(c, s) = std.striter(p.rest)
	p.rest = s
	-> c
}