var labels=new Object;
var macros=new Object;
var lp; // colon def link address
function mkWord(name,prim) {
    // word structure:
    // name, link, code (, parameter)
    M.push(name,lp);
    lp=M.length-1;
    M.push(new Prim(prim));
    macros[name]=[':'+name,prim];
    labels[name]=M.length;
}

function assemble(dat) {
	initialize();
    lp=0; // colon def link address
    // for native compilation
    var state=0;
    var nc="";
    var pw="";
    // break into lines for comments
    var lines=dat.split(/[\n\r]/);
    for(var l=0;l<lines.length;l++) {
        var words=lines[l].split(/\s+/);
        for(var i=0;i<words.length;i++) {
            // compiling native code
            if (state) {
                if (words[i] == "END-CODE") {
                    prims[pw]=new Function(nc);
                    state=0;
                    continue;
                }
                nc+=" "+words[i];
                continue;
            }
            if (words[i] == "JS-CODE") {
                state=1;
                pw=words[++i];
                nc="";
                continue;
            }
            if (words[i] == "") {continue;}
            // comments
            if (words[i] == "\\") {i=words.length; continue;}
            var fc=words[i].charAt(0);
            trace(">"+words[i]+"<");
            // define label
            if (fc == '#') {
                trace('label def');
                labels[words[i].substr(1)]=M.length;
                continue;
            }
            // use label
            if (fc == ':' && words[i].length > 1 ) {
                var lbl=words[i].substr(1);
                trace('label use');
                if (labels[lbl] != undefined) {
                    M.push(labels[lbl]);
                } else {
                    M.push({label: lbl});
                }
                continue;
            }
            // char literal
            if (fc == "'") {
                trace('char lit');
                M.push(words[i].charCodeAt(1));
                continue;
            }
            // string lit
            if (fc == '"') {
                trace('string lit');
                var str="";
                while(i<words.length && words[i].charAt(words[i].length-1) != '"') {
                    str+=words[i]+" ";
                    ++i;
                }
                str+=words[i];
                M.push(str.substr(1,str.length-2));
                continue;
            }
            // define macro
            /*
            if (fc == '{') {
                var mname=words[i].substr(1);
                macros[mname]=new Array;
                while(i<words.length && words[++i] != '}') {
                    macros[mname].push(words[i]);
                }
                continue;
            }
            */
            // col def: enhanced macro/label
            if (words[i] == ':') {
                mkWord(words[++i],'docol');
                continue;
            }
            if (words[i] == 'immediate') {
                M[lp+1].imm=true;
                continue;
            }
            if (words[i] == 'variable') {
                mkWord(words[++i],'literal');
                continue;
            }
            if (macros[words[i]] != undefined) {
                var mac=macros[words[i]];
                words=words.slice(0,i).concat(mac,words.slice(i+1));
                i--;
                continue;
            }
            if (prims[words[i]] != undefined) {
                M.push(new Prim(words[i]));
                continue;
            }
            var n=Number(words[i]);
            if (n == n) {
                trace('number '+n);
                M.push(n);
                continue;
            }
            trace('error');
            alert("error: unknown word "+words[i]);
            return;
        }
    }
    // resolve labels
    for(var i=0;i<M.length;i++) {
        if (M[i].label) {
           trace("resolve "+M[i].label+" to "+labels[M[i].label]);
            M[i]=labels[M[i].label];
        }
    }
    // set dp
    M[labels.dp]=lp;
}
