shithub: mc

ref: 3456a5b71887bb64aa8eac957a287a78b8ce9f28
dir: /parse.myr/

View raw version
use std

use "types.use"
use "util.use"
use "opts.use"
use "fsel.use"

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

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

	ap = std.vastart(&args)
	sl = std.bfmtv(buf[:], msg, ap)
	std.fput(1, "%s:%i: %s", p.fname, p.line, sl)
	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\n", 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 "test":	testtarget(p)
	| `std.Some "lib":	libtarget(p)
	| `std.Some "gen":	gentarget(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
}

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

/* testtarget: myrtarget */
const testtarget = {p
	p.targs = std.slpush(p.targs, `Test myrtarget(p, "test"))
}

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

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

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

/* gentarget: wordlist = wordlist ;; */
const gentarget = {p
	var outlist, cmdlist
	var gt

	match wordlist(p)
	| `std.None:	failparse(p, "gen target missing output files\n")
	| `std.Some out:
		outlist = out
	;;

	skipspace(p)
	if !matchc(p, '=')
		failparse(p, "expected '=' after '%s %s'\n", cmdlist, outlist[outlist.len-1])
	;;

	match wordlist(p)
	| `std.None:	failparse(p, "gen target missing command\n")
	| `std.Some cmd:
		cmdlist = cmd
	;;

	if !matchc(p, ';') || !matchc(p, ';')
		failparse(p, "expected ';;' terminating genfile command, got %c\n", peekc(p))
	;;

	gt = std.mk([.out = outlist, .cmd=cmdlist])
	for o in outlist
		std.htput(p.gensrc, o, gt)
	;;
	p.targs = std.slpush(p.targs, `Gen gt)
}

/*
myrtarget: name '=' inputlist ';;'
	| name attrlist = inputlist ';;'
*/
const myrtarget = {p, targ
	var ldscript, runtime, inst, incpath
	var name, inputs, libdeps, attrs
	var fsel

	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\n", targ, name)
		;;
	else
		attrs = [][:]
	;;

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

	match inputlist(p)
	| `std.Some (wl, libs): 
		fsel = mkfsel()
		libdeps = libs
		for w in wl
			fseladd(fsel, w)
		;;
		inputs = fselfin(fsel)
		std.slfree(wl)
	| `std.None: failparse(p, "expected list of file names after '%s %s'\n", targ, name)
	;;

	skipspace(p)
	if !matchc(p, ';') || !matchc(p, ';')
		failparse(p, "expected ';;' terminating input list, got %c\n", peekc(p))
	;;

	inst = true
	ldscript = ""
	runtime = ""
	incpath = [][:]
	for elt in attrs
		match elt
		| ("ldscript", lds):	ldscript = std.sldup(lds)
		| ("runtime", rt):	runtime = std.sldup(rt)
		| ("inc", path):	incpath = std.slpush(incpath, std.sldup(path))
		| ("noinst", val):
			if val.len != 0
				failparse(p, "noinst attr does not take argument\n")
			;;
			inst = false
		| (invalid, _):
			std.fatal(1, "got invalid attr '%s'\n", invalid)
		;;
	;;
	-> std.mk([
		.name=name,
		.inputs=inputs,
		.libdeps=libdeps,
		.install=inst,
		.ldscript=ldscript,
		.runtime=runtime,
		.incpath=incpath,
		.built=false
	])
}

/* anontarget: '=' wordlist ';;' */
const anontarget = {p, targ
	var inputs

	inputs = [][:]
	skipspace(p)
	if !matchc(p, '=')
		failparse(p, "expected '=' after '%s' target\n", 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, ';') || !matchc(p, ';')
		failparse(p, "expected ';;' terminating input list\n")
	;;
	-> inputs
}

/*
attrlist: attrs '}'

attrs	: EMPTY
	| attrs attr

attr	: name
	| name '=' name
*/
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, [][:]))
			;;
			if !matchc(p, ',')
				break
			;;
		| `std.None:	break
		;;
	;;
	if !matchc(p, '}')
		failparse(p, "expected '}' at end of attr list, got '%c'\n", peekc(p))
	;;
	if al.len == 0
		-> `std.None
	else
		-> `std.Some al
	;;
}

/*
inputlist: EMPTY
	| inputlist input

input	: word
	| "lib" word
*/
const inputlist = {p
	var wl, libs

	wl = [][:]
	libs = [][:]
	while true
		match word(p)
		| `std.Some "lib":
			match word(p)
			| `std.Some l:	libs = std.slpush(libs, l)
			| `std.None:	failparse(p, "expected lib name after 'lib'\n")
			;;
		| `std.Some w:	wl = std.slpush(wl, w)
		| `std.None:	break
		;;
	;;
	if wl.len == 0
		-> `std.None
	else
		-> `std.Some (wl, libs)
	;;
}

/* wordlist: EMPTY | wordlist word */
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
	;;
}

/* word: /wordchar*/
const word = {p : parser#
	var c, n
	var start

	skipspace(p)

	c = peekc(p)
	if c == '"'
		n = 0
		nextc(p)
		start = p.rest
		while p.rest.len > 0
			c = peekc(p)
			if c == '"'
				nextc(p)
				goto done
			elif c == '\\'
				c = nextc(p)
			;;
			nextc(p)
			n += std.charlen(c)
		;;
		failparse(p, "input ended within quoted word\n")
	else
		n = 0
		start = p.rest
		while p.rest.len > 0
			c = peekc(p)
			if wordchar(c)
				nextc(p)
				n += std.charlen(c)
			else
				break
			;;
		;;
	;;
:done
	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 == '~' || \
		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
}