Home 
 
# Expresso
#
# This code takes an expression, parses it, and then mutates it.
#
# The transformations are normally taken from a *.exp file (see default.exp
# for the default set of transformations).
#
# It involves a recursive descent parser, and a lot of uncommented code.
#

function webexpresso(gen,seed1,fn,x,...) {
	initialargs = 4
	ntrans = nargs() - initialargs
	transforms = []
	for ( n=0; n<ntrans; n++ ) {
		transforms[argv(initialargs+n)] = 1
	}
	if ( fn != "" ) {
		p = readmf(fn);
		# how many notes we want to pick
		howmany = 20
		p = onlynotes(p);
		lng1 = sizeof(p);
		if ( lng1 > howmany ) {
			rnd1 = rand(lng1-howmany)
			p = p{??.number >= rnd1}
			p = strip(head(p,howmany))
		}
	} else {
		p = phrase("'"+x+"'")
	}
	p = expresso(transforms,gen,seed1,p)
	print("EXPR = ",Gexpr,"<br><font size=-2><i>random seed used was",seed1,"<i><font>")

	limit = 5000
	psize = sizeof(p)
	if ( psize > limit ) {
		print("<p>NOTE: Generated phrase was too large (",psize,"), it has been truncated at ",limit," notes.")
		p = head(p,limit)
	}

	p.length = latest(p)

	writemf(p,"www.mid")
	writelines(p,"www.lines")
}

function etest(p) {
	if ( nargs() <= 0 ) {
		p = 'c,e,g'
	}
	return(expresso("default",10,currtime(),p))
	# print ("xx=",xx)
	# print("EXPR = ",Gexpr)
}

function expresso(transforms,gen,seed1,x) {

	if ( typeof(transforms) == "string" ) {
		fn = pathsearch(transforms+".exp")
		if ( fn == "" ) {
			print("Can't find file "+transforms+".exp")
			return('')
		}
		transforms = read_transforms(fn)
	}
	rand(-seed1)

	p = gen_expresso(transforms,gen,x,1)
	return(p)
}


Gtokes = []
Gtypes = []
GEN_EOF = -1
GEN_NOTHING = 0
GEN_NUMBER = "number"
GEN_STRING = "string"
GEN_WORD = "word"
GEN_BINOP = "binop"
GEN_UNOP = "unop"
GEN_PUNCT = "punct"

function gen_init_transforms(transforms) {
	n = 0;
	Gtrans_from = []
	Gtrans_to = []
	for ( k in transforms ) {
		b = split(k,"=")
		Gtrans_from[n] = gen_parsexp(b[0])
		Gtrans_to[n] = gen_parsexp(b[1])
		n++
	}
}

function read_transforms(fname) {
	arr = []
	f = open(fname)
	fifoctl(f,"type","l")	# read line-at-a-time
	while ( (ln=get(f)) != Eof ) {
		if ( substr(ln,1,1) != "#" ) {
			arr[ln] = 1
		}
	}
	return(arr)
}

function gran(transforms,g) {
	if ( nargs() < 1 )
		g = 10
	r = ''
	for ( n=0; n<g; n++ ) {
		r += gen_expresso(transforms,10,'cd12,c',0)
	}
	return(r)
}

function gen_expresso(transforms,ng,x,verbose) {

	if ( nargs() < 1 )
		ng = 3
	if ( nargs() < 2 )
		x='cd12,co4,c,c'
	if ( nargs() < 3 )
		verbose = 1

	gen_init_transforms(transforms)
	Gexpr = "X"
	pn = gen_parsexp(Gexpr)

	for ( i=0; i<ng; i++ ) {
		pn = gen_mutate(pn)
	}

	Gexpr = gen_sprint(pn)
	X=x
	eval "GEN="+Gexpr
	return(GEN)
}
function gen_parsexp(s) {
	gen_tokens(s)
	Gpeekn = 0
	gen_peek();	# to prime Glookahead
	return(gen_binopexpr())
}

function gen_isbinop(c) {
	return ( c ~~ "[-+*/|&]" );
}

function gen_subvar(oldvarname,subvarname,subexpr) {
	pn = [];
	op = subexpr["op"]
	if ( op == "var" ) {
		pn["op"] = op
		if ( subexpr["var"] == subvarname ) {
			pn["var"] = oldvarname
		} else {
			pn["var"] = subexpr["var"]
		}
	} else if ( gen_isbinop(op) ) {
		pn["op"] = op
		pn["operand1"] = gen_subvar(oldvarname,subvarname,subexpr["operand1"])
		pn["operand2"] = gen_subvar(oldvarname,subvarname,subexpr["operand2"])

	} else if ( op == "func" ) {
		pn["op"] = op
		pn["func"] = subexpr["func"]
		i = 1
		as = "arg"+string(i)
		while ( as in subexpr ) {
			pn[as] = gen_subvar(oldvarname,subvarname,subexpr[as])
			as = "arg"+string(++i)
		}
	} else if ( op == "number" || op == "string") {
		pn = arraycopy(subexpr)
	} else {
print("WARNING: gen_subvar isn't handling subexpr[op]=",subexpr["op"],", using original var")
		pn["op"] = subexpr["op"]
		pn["var"] = oldvarname
	}
	return(pn)
}

function gen_mutate(pn) {
	if ( typeof(pn) != "array" ) {
		printf("NONARRAY(",pn,")")
	} else if ( pn["op"] == "var" ) {

		# Look through the transformations to see how many
		# "var" ones we have
		nv = 0
		for ( k in Gtrans_from ) {
			pf = Gtrans_from[k]
			if ( pf["op"] == "var" )
				nv++
		}
		prob = 100/nv;
		for ( k in Gtrans_from ) {
			pf = Gtrans_from[k]
			if ( pf["op"] == "var" && (rand(100) < prob) ) {
				# apply it - save the old variable name
				oldvarname = pn["var"]
				subvarname = pf["var"]
				# delete the original array
				for ( i in pn )
					delete(pn[i])
				# copy the transformation, replacing
				# all the instances of subvarname with
				# the oldvarname
				pn = gen_subvar(oldvarname,subvarname,Gtrans_to[k])
				break;
			}
		}

	} else if ( pn["op"] == "number" ) {
		# printf(pn["number"])
	} else if ( pn["op"] == "func" ) {
		# gen_mutate(pn["func"])
		for ( n=1; ; n++ ) {
			a = "arg"+string(n)
			if ( a in pn ) {
				pn[a] = gen_mutate(pn[a])
			} else {
				break
			}
		}
	} else if ( gen_isbinop(pn["op"]) ) {
		# op = pn["op"]
		# if ( op == "+" )
		# 	pn["op"] = "-"
		# else if ( op == "-" )
		# 	pn["op"] = "+"
		pn["operand1"] = gen_mutate(pn["operand1"])
		pn["operand2"] = gen_mutate(pn["operand2"])
	} else {
		# printf(pn["op"])
	}
	return(pn)
}

function gen_sprint(pn) {
	if ( typeof(pn) != "array" ) {
		return("RAW("+pn+")")
	} else if ( pn["op"] == "!" ) {
		return("!("+gen_sprint(pn["operand1"])+")")
	} else if ( pn["op"] == "var" ) {
		return(pn["var"])
	} else if ( pn["op"] == "number" ) {
		return(pn["number"])
	} else if ( pn["op"] == "func" ) {
		r = pn["func"]+"("
		sep = ""
		for ( n=1; ; n++ ) {
			a = "arg"+string(n)
			if ( a in pn ) {
				if ( sep != "" )
					r += sep
				r += gen_sprint(pn[a])
			} else {
				break
			}
			sep = ","
		}
		r += ")"
		return(r)
	} else if ( gen_isbinop(pn["op"]) ) {
		return("("
			+gen_sprint(pn["operand1"])
			+pn["op"]
			+gen_sprint(pn["operand2"])
			+")")
	} else {
		return("(RAWOP=pn="+string(pn)+")")
	}
}

function gen_peek() {
	if ( Gpeekn < sizeof(Gtokes) ) {
		Glookahead = Gtokes[Gpeekn]
		Glookaheadtype = Gtypes[Gpeekn]
	} else {
		Glookahead = EOF
		Glookaheadtype = GEN_EOF
	}
}

function gen_eat() {
	w = Gtokes[Gpeekn++]
	gen_peek()
	return(w)
	
}


function gen_binopexpr() {
	t1 = gen_val_expr();
	while ( Glookaheadtype == GEN_BINOP ) {
		op = gen_eat()
		t2 = gen_val_expr();
		tree = []
		tree["op"] = op
		tree["operand1"] = t1
		tree["operand2"] = t2
		t1 = tree
	}
	return(t1)
}

function gen_val_expr() {
	tree = []
	if ( Glookahead == "(" ) {
		gen_match("(");
		n = gen_binopexpr();
		gen_match(")");
		return(n)
	}
	if ( Glookaheadtype == GEN_UNOP ) {
		tree["op"] = gen_eat()
		tree["operand1"] = gen_binopexpr();
		return(tree)
	}
	if ( Glookaheadtype == GEN_NUMBER ) {
		tree["op"] = "number"
		tree["number"] = gen_eat()
		return(tree)
	}
	if ( Glookaheadtype == GEN_WORD ) {
		w1 = gen_eat()
		if ( Glookahead == "(" ) {
			gen_eat()
			tree["op"] = "func"
			tree["func"] = w1;
			na = 0;
			if ( Glookahead != ")" ) {
			    while ( 1 ) {
				n2 = gen_binopexpr();
				na++
				tree["arg"+string(na)] = n2
				if ( Glookahead == "," ) {
					gen_match(",")
				} else if ( Glookahead == ")" ) {
					gen_match(")")
					break;
				} else {
					print("Unexpected input: "+Glookahead)
					return([])
				}
			    }
			}
		} else {
			tree["op"] = "var"
			tree["var"] = w1
		}
		return(tree)
	}
	return(tree)
}

function gen_match(c) {
	if ( Gtokes[Gpeekn] == c ) {
		Gpeekn++
		Glookahead = Gtokes[Gpeekn];
		Glookaheadtype = Gtypes[Gpeekn];
	} else {
		print("Didn't find expected token: ",c);
	}
}

function gen_tokens(s) {
	current = ""
	lng = sizeof(s)
	Gtokes = []
	Gtypes = []
	ntokes = 0
	inside = 0
	for ( n=0; n<lng; n++ ) {
		c = substr(s,n+1,1)
		if ( inside == GEN_STRING ) {
			current += c
			if ( c == substr(current,1,1) ) {
				Gtokes[ntokes] = current
				ntokes++
				inside = GEN_NOTHING
				current = ""
			}
			continue
		}

		if ( inside == GEN_NUMBER ) {
			if ( c ~~ "[0123456789.]" ) {
				current += c
				continue
			}
			Gtokes[ntokes] = current
			ntokes++
		}

		if ( inside == GEN_WORD ) {
			if ( c ~~ "[a-zA-Z]" ) {
				current += c
				continue
			}
			Gtokes[ntokes] = current
			ntokes++
		}

		inside = GEN_NOTHING
		current = ""

		if ( c == "(" || c == ")" || c == "," ) {
			Gtypes[ntokes] = GEN_PUNCT
			Gtokes[ntokes] = c
			ntokes++
			current = ""
		} else if ( c == "!" ) {
			Gtypes[ntokes] = GEN_UNOP
			Gtokes[ntokes] = c
			ntokes++
			current = ""
		} else if ( c == "-" ) {
			cnext = substr(s,n+2,1)
			if ( cnext ~~ "[0123456789]" ) {
				Gtypes[ntokes] = GEN_NUMBER
				inside = GEN_NUMBER
				current = "-"+cnext
				n++
			} else {
				Gtypes[ntokes] = GEN_UNOP
				Gtokes[ntokes] = c
				ntokes++
			}
		} else if ( gen_isbinop(c) ) {
			Gtypes[ntokes] = GEN_BINOP
			Gtokes[ntokes] = c
			ntokes++
			current = ""
		} else if ( c ~~ "[0123456789]" ) {
			Gtypes[ntokes] = GEN_NUMBER
			inside = GEN_NUMBER
			current = c
		} else if ( c ~~ "[a-zA-Z]" ) {
			Gtypes[ntokes] = GEN_WORD
			inside = GEN_WORD
			current = c
		} else if ( c ~~ "\"" || c ~~ "'" ) {
			current = c
			Gtypes[ntokes] = GEN_STRING
			inside = GEN_STRING
		} else {
			current += c
		}
			
	}
	if ( current != "" )
		Gtokes[ntokes++] = current
	return()
}