Import aap@github's B compiler.
authorDavid Given <dg@cowlark.com>
Sun, 27 Nov 2016 10:37:25 +0000 (11:37 +0100)
committerDavid Given <dg@cowlark.com>
Sun, 27 Nov 2016 10:37:25 +0000 (11:37 +0100)
16 files changed:
lang/b/distr/LICENSE [new file with mode: 0644]
lang/b/distr/Makefile [new file with mode: 0644]
lang/b/distr/README.md [new file with mode: 0644]
lang/b/distr/abc [new file with mode: 0755]
lang/b/distr/b.h [new file with mode: 0644]
lang/b/distr/b0.c [new file with mode: 0644]
lang/b/distr/b1.c [new file with mode: 0644]
lang/b/distr/brt.s [new file with mode: 0644]
lang/b/distr/examples/1_var.b [new file with mode: 0644]
lang/b/distr/examples/2_ext.b [new file with mode: 0644]
lang/b/distr/examples/3_fun.b [new file with mode: 0644]
lang/b/distr/examples/4_goto.b [new file with mode: 0644]
lang/b/distr/examples/5_while.b [new file with mode: 0644]
lang/b/distr/examples/printargs.b [new file with mode: 0644]
lang/b/distr/lib.b [new file with mode: 0644]
lang/b/distr/link.ld [new file with mode: 0644]

diff --git a/lang/b/distr/LICENSE b/lang/b/distr/LICENSE
new file mode 100644 (file)
index 0000000..db5ca3b
--- /dev/null
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2016 aap
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/lang/b/distr/Makefile b/lang/b/distr/Makefile
new file mode 100644 (file)
index 0000000..2838486
--- /dev/null
@@ -0,0 +1,15 @@
+CFLAGS=-Wall -Wextra
+b: b0.o b1.o
+       cc b0.o b1.o -o b
+b0.o: b0.c b.h
+b1.o: b1.c b.h
+
+libs:
+       ./abc -c brt.s lib.b
+
+install: b abc
+       cp abc $(HOME)/bin
+
+%.o: %.s
+       as --32 $^ -o $@
+
diff --git a/lang/b/distr/README.md b/lang/b/distr/README.md
new file mode 100644 (file)
index 0000000..d2ab5fa
--- /dev/null
@@ -0,0 +1,53 @@
+A B Compiler
+============
+
+abc is a compiler for the [B Programming Language](http://en.wikipedia.org/wiki/B_(programming_language)) that targets x86\_32 processors. It is currently tested under Linux but should work (or at least be easily ported) to other UNIX-like systems. The code is based on [an early C compiler (last1120c)](http://www.cs.bell-labs.com/who/dmr/primevalC.html) by Dennis Ritchie.
+
+Documentation
+-------------
+
+* [The Programming Language B](http://9p.io/cm/cs/who/dmr/bintro.html)
+
+* [B Reference by Ken Thompson](http://9p.io/cm/cs/who/dmr/kbman.html) describes a presumably earlier variant of B, which is slightly different from the one described above. The compiler cannot understand it, but I plan to implement a compatibility mode (the differences are minor).
+
+Implementation
+--------------
+
+Since B was first implemented for machines with word addressing, some hacking was required to make it work on the byte addressed x86. Addresses filled in by the linker are always byte addresses, so pointers to these addresses are collectively stored at the end of the .data section and are then converted to word addresses at runtime, before main() is called.
+
+The generated assembly is *very* inefficient, not even constant expressions are reduced at compile time. Also I/O is currently not buffered.
+
+How to use
+----------
+
+The installation requires a little configuration:
+'abc' is a frontend for the actual compiler which feels somewhat like gcc (it also handles assembling and linking). Before you can use it, set it's BDIR variable to the directory of the B compiler.
+In the Makefile, change the directory of the 'install' rule to wherever you want your 'abc' file to reside.
+Then type
+
+       make install libs
+
+which compiles the compiler 'b', installs the 'abc' frontend and compiles the B runtime and library (brt.o and lib.o).
+
+To compile and link a B program, simply type
+
+       abc -o outfile file1.b [file2.b ...]
+
+If you want to compile and assemble only:
+
+       abc -c file1.b [file2.b ...]
+
+or generate only the assembly:
+
+       abc -S file1.b [file2.b ...]
+
+Examples of B programs are in the 'examples' directory, they are mostly from Brian Kernighan's tutorial.
+
+Bugs
+----
+
+Since command line parameters aren't passed word-aligned, B can't handle them easily. brt.s copies the strings to another location and aligns them, the space is not dynamically allocated however and only 256 bytes are available by default.
+
+The library is incomplete but has some of the most important functions.
+
+I have only tested the compiler on an x86\_64 gentoo system.
diff --git a/lang/b/distr/abc b/lang/b/distr/abc
new file mode 100755 (executable)
index 0000000..21cdda4
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+BDIR="$HOME/abc"
+objs="$BDIR/brt.o $BDIR/lib.o"
+BC="$BDIR/b"
+
+# compile in.b [out.s]
+compile() {
+       if [ "${1##*.}" != "b" ]; then
+               echo "Error: can only compile b files" >&2
+               exit 1
+       fi
+       cout=$2
+       [ "$cout" != "" ] || cout=${1%b}s
+       tmp1=`mktemp`; tmp2=`mktemp`
+       $BC $1 $tmp2 $tmp1
+       retval=$?
+       cat $tmp1 $tmp2 > $cout
+       rm $tmp1 $tmp2
+       [ $retval -eq 0 ] || rm $cout && return $retval
+       echo $cout
+       return $retval
+}
+
+# assemble in.{sb} [out.o]
+assemble() {
+       atmp=""
+       ain=$1
+       aout=$2;
+       if [ "${1##*.}" = "b" ]; then
+               [ "$aout" != "" ] || aout=${ain%b}o
+               ain=`mktemp --suffix=.s`
+               compile $1 $ain >/dev/null || return 1
+               atmp="foo"
+       elif [ "${1##*.}" = "s" ]; then
+               [ "$aout" != "" ] || aout=${ain%s}o
+       else
+               echo "Error: can only compile b and s files" >&2
+               exit 1
+       fi
+       as --32 -g $ain -o $aout
+       [ "$atmp" != "" ] && rm $ain
+       echo $aout
+}
+
+out=""
+action="link"
+while getopts "o:Sc" o
+do     case "$o" in
+       o)      out="$OPTARG";;
+       S)      action=compile;;
+       c)      action=assemble;;
+       esac
+done
+shift $(($OPTIND - 1))
+
+# ignore -o option if more than one file given and not linking objs
+if [ $# -gt 1 ]; then
+       if [ "$action" != "link" ]; then
+               out=""
+       fi
+fi
+
+[ $# -ne 1 ] && havelist=yes
+tmpobjs=""
+for i in $@; do
+       if [ "$action" != "link" ]; then
+               [ "$havelist" = "yes" ] && echo $i:
+               $action $i $out >/dev/null
+               [ $? -eq 0 ] || break=1
+       else
+               if [ "${i##*.}" = "o" ]; then
+                       objs="$objs $i"
+               else
+                       [ "$havelist" = "yes" ] && echo $i:
+                       ltmp=`mktemp --suffix=.o`
+                       tmpobjs="$tmpobjs $ltmp"
+                       assemble $i $ltmp >/dev/null
+                       [ $? -eq 0 ] || break=1
+               fi
+       fi
+done
+if [ $break ]; then
+       [ "$tmpobjs" = "" ] || rm $tmpobjs
+       echo "Error" >&2
+       exit 1
+fi
+if [ "$action" = "link" ]; then
+       if [ "$out" = "" ]; then
+               out="-o a.out"
+       else
+               out="-o $out"
+       fi
+       ld -m elf_i386 -T $BDIR/link.ld $out $objs $tmpobjs
+       rm $tmpobjs
+fi
diff --git a/lang/b/distr/b.h b/lang/b/distr/b.h
new file mode 100644 (file)
index 0000000..9aae946
--- /dev/null
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define        NCPS    8       /* chars per symbol */
+#define        NCPW    4       /* chars per word */
+#define        ALIGN   4       /* Passed directly to the assembler's .align */
+#define        HSHSIZ  400     /* hash table size */
+#define        SWSIZ   230     /* switch table size */
+#define        CMSIZ   40      /* symbol stack size */
+#define        SSIZE   20      /* operator and precedence stack size */
+#define        OSSIZ   300*8   /* space for expression tree */
+
+#define        EOS     04      /* end of string marker */
+
+/*
+ * Holds a B symbol.
+ * class is one of the storage classes below.
+ * offset is used depending on class.
+ */
+struct hshtab {
+       int     class;
+       int     offset;
+       int     dim;
+       struct  hshtab *next;
+       char    name[NCPS+1];
+};
+
+struct tnode {
+       int     op;
+       int     value;
+       struct tnode *tr1;
+       struct tnode *tr2;
+};
+
+struct swtab {
+       int     swlab;
+       int     swval;
+};
+
+struct hshtab hshtab[HSHSIZ];
+int    hshused;
+int    eof;
+int    peekc;
+char   ctab[128];
+struct hshtab *bsym;
+struct hshtab *paraml, *parame;
+int    cval;
+int    isn;
+char   symbuf[NCPS+1];
+FILE   *sbufp;
+int    stack;
+struct tnode **cp;
+int    *space;
+int    ospace[OSSIZ];
+int    retlab;
+int    nerror;
+struct swtab swtab[SWSIZ];
+struct swtab *swp;
+int    deflab;
+extern int     contlab;
+extern int     brklab;
+
+extern int opdope[];
+extern int line;
+extern int peeksym, peeksym2;
+
+void error(char *s, ...);
+void printtoken(int tok, FILE *out);
+struct tnode * block(int op, int value, struct tnode *tr1, struct tnode *tr2);
+void rcexpr(struct tnode *tr);
+void cbranch(struct tnode *t, int lab, int val);
+void jump(int lab);
+void label(int l);
+
+#define        EOFC    0
+#define        SEMI    1
+#define        LBRACE  2
+#define        RBRACE  3
+#define        LBRACK  4
+#define        RBRACK  5
+#define        LPARN   6
+#define        RPARN   7
+#define        COLON   8
+#define        COMMA   9
+
+#define        MCALL   15
+#define        CALL    16
+#define        DECBEF  17
+#define        INCBEF  18
+#define        DECAFT  19
+#define        INCAFT  20
+#define        EXCLA   21
+#define        NEG     22
+#define        AMPER   23
+#define        STAR    24
+#define        QUEST   25
+
+#define        PLUS    30
+#define        MINUS   31
+#define        MOD     32
+#define        TIMES   33
+#define        DIVIDE  34
+#define        OR      35
+#define        AND     36
+#define        LSHIFT  37
+#define        RSHIFT  38
+#define        EQUAL   39
+#define        NEQUAL  40
+#define        LESSEQ  41
+#define        LESS    42
+#define        GREATEQ 43
+#define        GREAT   44
+
+#define        ASSIGN  49
+#define        ASPLUS  50
+#define        ASMINUS 51
+#define        ASMOD   52
+#define        ASTIMES 53
+#define        ASDIV   54
+#define        ASOR    55
+#define        ASAND   56
+#define        ASLSH   57
+#define        ASRSH   58
+#define        ASEQUAL 59
+#define        ASNEQL  60
+#define        ASLEQ   61
+#define        ASLESS  62
+#define        ASGTQ   63
+#define        ASGREAT 64
+
+#define        CON     65
+#define        STRING  66
+#define        NAME    67
+#define        KEYW    68
+
+#define        SQUOTE  121
+#define        DQUOTE  122
+#define        NEWLN   123
+#define        SPACE   124
+#define        LETTER  125
+#define        DIGIT   126
+#define        UNKN    127
+
+#define        SEOF    200
+
+/* storage classes */
+#define AUTO   1
+#define EXTERN 2
+#define INTERN 3
+#define ARG    4
+#define KEYWF  5
+
+/* keywords */
+#define        CASE    3
+#define        IF      4
+#define        ELSE    5
+#define        WHILE   6
+#define        SWITCH  7
+#define        GOTO    8
+#define        RETURN  9
+#define        DEFAULT 10
+#define        BREAK   11
+
diff --git a/lang/b/distr/b0.c b/lang/b/distr/b0.c
new file mode 100644 (file)
index 0000000..424e1af
--- /dev/null
@@ -0,0 +1,1277 @@
+#include "b.h"
+
+void extdef(void);
+struct hshtab * lookup(void);
+void blkhed(void);
+void blkend(void);
+void retseq(void);
+void statement(int d);
+struct tnode * tree(void);
+void errflush(int o);
+
+int    line = 1;
+int    peeksym = -1, peeksym2 = -1;;
+int    contlab = -1;
+int    brklab = -1;
+
+void
+init(char *s, int val)
+{
+       char *sp;
+       struct hshtab *np;
+
+       sp = symbuf;
+       while (sp < symbuf+NCPS+1)
+               if ((*sp++ = *s++) == '\0')
+                       s--;
+       np = lookup();
+       np->class = KEYWF;
+       np->offset = val;
+}
+
+int
+main(int argc, char *argv[])
+{
+       if (argc < 3) {
+               error("Arg count");
+               exit(1);
+       }
+       if (freopen(argv[1], "r", stdin) == NULL) {
+               error("Can't find %s", argv[1]);
+               exit(1);
+       }
+       if ((sbufp=fopen(argv[2], "w")) == NULL) {
+               error("Can't create %s", argv[2]);
+               exit(1);
+       }
+       if (argc > 3) {
+               if (freopen(argv[3], "w", stdout) == NULL) {
+                       error("Can't create %s", argv[2]);
+                       exit(1);
+               }
+       }
+       init("auto", AUTO);
+       init("extrn", EXTERN);
+       init("case", CASE);
+       init("if", IF);
+       init("else", ELSE);
+       init("while", WHILE);
+       init("switch", SWITCH);
+       init("goto", GOTO);
+       init("return", RETURN);
+       init("default", DEFAULT);
+       init("break", BREAK);
+       fprintf(sbufp, "\t.data\n");
+       while (!eof) {
+               extdef();
+               blkend();
+       }
+       return nerror != 0;
+}
+
+/*
+ * Lexer
+ */
+
+int
+spnextchar(void)
+{
+       int c;
+
+       if ((c = peekc) == 0)
+               c = getchar();
+       if (c == '\t')
+               c = ' ';
+       else if (c == '\n') {
+               c = ' ';
+               line++;
+       }
+       peekc = c;
+       return c;
+}
+
+int
+nextchar(void)
+{
+       while (spnextchar() == ' ')
+               peekc = 0;
+       return peekc;
+}
+
+int
+subseq(int c, int a, int b)
+{
+       if (spnextchar() != c)
+               return a;
+       peekc = 0;
+       return b;
+}
+
+/* Only decimal and octal bases, could extend */
+int
+getnum(void)
+{
+       int base;
+       int c;
+
+       base = 10;
+       cval = 0;
+       if ((c=spnextchar()) == '0')
+               base = 8;
+       for (; ctab[c] == DIGIT; c = getchar())
+               cval = cval*base + c-'0';
+       peekc = c;
+       return CON;
+}
+
+int
+mapch(char c)
+{
+       int a;
+
+       if ((a=getchar()) == c)
+               return -1;
+       switch (a) {
+
+       case '\n':
+       case '\0':
+               error("Nonterminated string");
+               peekc = a;
+               return -1;
+
+       case '*':
+               switch (a=getchar()) {
+
+               case 't':
+                       return('\t');
+
+               case 'n':
+                       return('\n');
+
+               case '0':
+                       return('\0');
+
+               case '(':
+                       return('{');
+
+               case ')':
+                       return('}');
+
+               case 'e':
+                       return(EOS);
+
+               case '\n':
+                       line++;
+                       return('\n');
+               }
+       }
+       return a;
+}
+
+int
+getcc(void)
+{
+       char *cp;
+       int c, cc;
+
+       cval = 0;
+       cc = 0;
+       cp = (char*) &cval;
+       while ((c = mapch('\'')) >= 0)
+               if (cc++ < NCPW)
+                       *cp++ = c;
+       if (cc > NCPW)
+               error("Long character constant");
+       return CON;
+}
+
+int
+getstr(void)
+{
+       int c;
+       int i;
+
+       fprintf(sbufp, "\t.align 4\n");
+       fprintf(sbufp, "L%d:", cval = isn++);
+       if ((c = mapch('"')) >= 0)
+               fprintf(sbufp, "\t.byte %04o", c);
+       for (i = 2; (c = mapch('"')) >= 0; i++)
+               fprintf(sbufp, ",%04o", c);
+       fprintf(sbufp, ",04");
+       while ((i++%4) != 0)
+               fprintf(sbufp, ",00");
+       fprintf(sbufp, "\n");
+       return STRING;
+}
+
+struct hshtab *
+lookup(void)
+{
+       int i;
+       char *sp, *np;
+       struct hshtab *rp;
+
+       i = 0;
+       sp = symbuf;
+       while (sp < symbuf+NCPS)
+               i += *sp++&0177;
+       rp = &hshtab[i%HSHSIZ];
+       while (*(np = rp->name)) {
+               for (sp=symbuf; sp < symbuf+NCPS;)
+                       if (*np++ != *sp++)
+                               goto no;
+               return rp;
+       no:
+               if (++rp >= &hshtab[HSHSIZ])
+                       rp = hshtab;
+       }
+       if (++hshused > HSHSIZ) {
+               error("Symbol table overflow");
+               exit(1);
+       }
+       rp->class = 0;
+       rp->offset = 0;
+       rp->dim = 0;
+       sp = symbuf;
+       for (np = rp->name; sp < symbuf+NCPS+1;)
+               *np++ = *sp++;
+       return rp;
+}
+
+/*
+ * Symbol peeking with one peeksym doesn't work if an ASSIGN is only peeked,
+ * since it itself peeks a symbol, which is then overwritten.
+ */
+
+/* Note: does not push bsyms !! */
+int
+pushsym(int sym)
+{
+       if (peeksym < 0)
+               peeksym = sym;
+       else if (peeksym2 < 0) {
+               peeksym2 = peeksym;
+               peeksym = sym;
+       } else
+               error("Cannot push more than two symbols\n");
+       return sym;
+}
+
+int
+symbol(void)
+{
+       int c;
+       char *sp;
+
+       if (peeksym >= 0) {
+               c = peeksym;
+               peeksym = peeksym2;
+               peeksym2 = -1;
+               return c;
+       }
+       if (peekc) {
+               c = peekc;
+               peekc = 0;
+       } else
+               if (eof)
+                       return EOFC;
+               else
+                       c = getchar();
+       if (c==EOF) {
+               eof++;
+               return(EOFC);
+       }
+
+loop:
+       switch (ctab[c]) {
+
+       case NEWLN:
+               line++;
+
+       case SPACE:
+               c = getchar();
+               goto loop;
+
+       case PLUS:
+               return subseq(c,PLUS,INCBEF);
+
+       case MINUS:
+               return subseq(c,MINUS,DECBEF);
+
+       case LESS:
+               if (subseq(c,0,1))
+                       return LSHIFT;
+               return subseq('=', LESS, LESSEQ);
+
+       case GREAT:
+               if (subseq(c,0,1))
+                       return RSHIFT;
+               return subseq('=', GREAT, GREATEQ);
+
+       case ASSIGN:
+               if (subseq(' ',0,1))
+                       return ASSIGN;
+               /* avoid peeking a name, which could overwrite
+                * an already set bsym. */
+               if (ctab[peekc = spnextchar()] == LETTER)
+                       return ASSIGN;
+               c = symbol();
+               if (PLUS <= c && c <= GREAT)
+                       return c + ASPLUS-PLUS;
+               if (c == ASSIGN)
+                       return EQUAL;
+               pushsym(c);
+               return ASSIGN;
+
+       case EXCLA:
+               return subseq('=',EXCLA,NEQUAL);
+
+       case DIVIDE:
+               if (subseq('*',1,0))
+                       return DIVIDE;
+               while ((c = spnextchar()) != EOFC) {
+                       peekc = 0;
+                       if (c == '*') {
+                               if (spnextchar() == '/') {
+                                       peekc = 0;
+                                       c = getchar();
+                                       goto loop;
+                               }
+                       }
+               }
+               eof++;
+               error("Nonterminated comment");
+               return EOFC;
+
+       case DIGIT:
+               peekc = c;
+               return getnum();
+
+       case SQUOTE:
+               return(getcc());
+
+       case DQUOTE:
+               return(getstr());
+
+       case LETTER:
+               sp = symbuf;
+               while (ctab[c] == LETTER || ctab[c] == DIGIT) {
+                       if (sp < symbuf+NCPS)
+                               *sp++ = c;
+                       c = getchar();
+               }
+               while (sp < symbuf+NCPS+1)
+                       *sp++ = '\0';
+               peekc = c;
+               bsym = lookup();
+               if (bsym->class == KEYWF) {
+                       cval = bsym->offset;
+                       return KEYW;
+               }
+               return NAME;
+
+       case UNKN:
+               error("Unknown character");
+               c = getchar();
+               goto loop;
+       }
+       return (ctab[c]);
+}
+
+/*
+ * Declarations and Definitions
+ */
+
+/* Declares a list of names to be of storage class "kw". */
+void
+declare(int kw)
+{
+       int o;
+
+       while ((o = symbol()) == NAME) {
+               if (bsym->class)
+                       error("%s redeclared", bsym->name);
+               bsym->class = kw;
+               while ((o = symbol()) == LBRACK) {
+                       if ((o = symbol()) == CON) {
+                               if (bsym->dim)
+                                       error("Bad vector");
+                               bsym->dim = cval + 1;
+                               o = symbol();
+                       }
+                       if (o != RBRACK)
+                               goto syntax;
+               }
+               if (kw == ARG) {
+                       bsym->next = NULL;
+                       if (!paraml)
+                               paraml = bsym;
+                       else
+                               parame->next = bsym;
+                       parame = bsym;
+               }
+               if (o != COMMA)
+                       break;
+       }
+       if ((o == SEMI && kw != ARG) || (o == RPARN && kw == ARG))
+               return;
+syntax:
+       error("Declaration syntax");
+       errflush(o);
+}
+
+void
+declist(void)
+{
+       int o;
+
+       while ((o = symbol()) == KEYW && (cval == AUTO || cval == EXTERN))
+               declare(cval);
+       pushsym(o);
+}
+
+void
+function(void)
+{
+       printf("\tpush\t%%ebp\n");
+       printf("\tmov\t%%esp,%%ebp\n");
+
+       declare(ARG);
+       statement(1);
+       retseq();
+}
+
+void
+global(char *s)
+{
+       printf("\t.globl\t_%s\n", s);
+       printf("\t.data\n");
+       printf("\t.align %d\n", ALIGN);
+}
+
+void
+bsymb(char *s, int und)
+{
+       printf("\t.section .bsymb; .long %s%s; .data\n", und?"_":"", s);
+}
+
+void
+extdef(void)
+{
+       int o, dim, i;
+       char *bs;
+
+       if ((o = symbol()) == EOFC || o == SEMI)
+               return;
+       if (o != NAME)
+               goto syntax;
+       bs = bsym->name;
+       i = dim = 0;
+       switch(o = symbol()) {
+
+       case SEMI:
+               printf("\t.comm\t_%s,%d,%d\n", bs, NCPW, ALIGN);
+               goto done;
+
+       /* init */
+       case CON:
+       case STRING:
+               global(bs);
+               if (o == STRING)
+                       bsymb(bs,1);
+               printf("_%s:", bs);
+               pushsym(o);
+               goto init;
+
+       /* vector */
+       case LBRACK:
+               if ((o=symbol()) == CON) {
+                       dim = cval + 1;
+                       o=symbol();
+               }
+               if (o != RBRACK)
+                       goto syntax;
+               global(bs);
+               if ((o=symbol()) == SEMI) {
+                       printf("\t.comm\tL%d,%d,%d\n", isn, dim*NCPW, ALIGN);
+                       bsymb(bs,1);
+                       printf("_%s:\t.long L%d\n", bs, isn++);
+                       goto done;
+               }
+               bsymb(bs,1);
+               printf("_%s:\t.long 1f\n1:", bs);
+               pushsym(o);
+
+       init:
+               do {
+                       if ((o=symbol()) != CON && o != STRING && o != NAME)
+                               goto syntax;
+                       if (o == NAME) {
+                               bsymb("1f",0);
+                               printf("1:\t.long _%s\n", bsym->name);
+                       } else
+                               printf("\t.long %s%d\n", o==STRING?"L":"",cval);
+                       i++;
+               } while ((o=symbol()) == COMMA);
+               dim = (i > dim) ? i : dim;
+               if (dim - i)
+                       printf("\t.zero %d\n", (dim-i)*NCPW);
+               if (o == SEMI)
+                       goto done;
+               goto syntax;
+
+       /* function */
+       case LPARN:
+               global(bs);
+               bsymb(bs,1);
+               printf("_%s:\t.long 1f\n", bs);
+               printf("\t.text\n\t.align 4\n1:");
+               function();
+       done:
+               printf("\n");
+               return;
+
+       case EOFC:
+               return;
+       }
+syntax:
+       error("External definition syntax");
+       printtoken(o, stderr);
+       errflush(o);
+       statement(0);
+}
+
+void
+setstk(int a)
+{
+       int dif;
+
+       dif = stack-a;
+       stack = a;
+       if (dif)
+               printf("\tsub\t$%d, %%esp\n", dif);
+}
+
+void
+defvec(void)
+{
+       stack -= NCPW;
+       printf("\tmov\t%%esp,%%eax\n");
+       printf("\tshr\t$2,%%eax\n");
+       printf("\tpush\t%%eax\n");
+}
+
+void
+blkhed(void)
+{
+       int al, pl;
+       struct hshtab *bs;
+
+       declist();
+       stack = al = -4;
+       pl = 8;
+       while (paraml) {
+               paraml = (bs = paraml)->next;
+               bs->offset = pl;
+               pl += NCPW;
+       }
+       for (bs = hshtab; bs < &hshtab[HSHSIZ]; bs++)
+               if (bs->name[0]) {
+                       if (bs->class == AUTO) {
+                               bs->offset = al;
+                               if (bs->dim) {
+                                       al -= bs->dim*NCPW;
+                                       setstk(al);
+                                       defvec();
+                                       bs->offset = al;
+                               }
+                               al -= NCPW;
+                       } else if (bs->class == ARG)
+                               bs->class = AUTO;
+               }
+       setstk(al);
+}
+
+void
+blkend(void)
+{
+       struct hshtab *np;
+
+       for (np = hshtab; np < &hshtab[HSHSIZ]; np++)
+               if (np->class != KEYWF) {
+                       np->name[0] = '\0';
+                       hshused--;
+               }
+}
+
+/*
+ * Statements and Expressions
+ */
+
+struct tnode *
+pexpr(void)
+{
+       struct tnode *t;
+       int o;
+
+       if ((o = symbol()) != LPARN)
+               goto syntax;
+       t = tree();
+       if ((o = symbol()) != RPARN)
+               goto syntax;
+       return t;
+syntax:
+       error("Statement syntax");
+       errflush(o);
+       return NULL;
+}
+
+void
+label(int l)
+{
+       printf("L%d:\n", l);
+}
+
+void
+retseq(void)
+{
+       printf("\tjmp\tretrn\n");
+}
+
+/* Jump to "lab", if the expression "t" evaluated to "val". */
+void
+cbranch(struct tnode *t, int lab, int val)
+{
+       rcexpr(t);
+       if (val == 0)
+               printf("\ttest\t%%eax,%%eax\n");
+       else
+               printf("\tcmp\t%%eax,$%d\n", val);
+       printf("\tje\tL%d\n", lab);
+}
+
+void
+jump(int lab)
+{
+       printf("\tjmp\tL%d\n", lab);
+}
+
+void
+pswitch(void)
+{
+       struct swtab *sswp;
+       int dl, swlab;
+
+       sswp = swp;
+       if (swp == NULL)
+               swp = swtab;
+       swlab = isn++;
+       printf("\tmov\t$L%d,%%ebx\n", swlab);
+       printf("\tjmp\tbswitch\n");
+       dl = deflab;
+       deflab = 0;
+       statement(0);
+       if (!deflab) {
+               deflab = isn++;
+               label(deflab);
+       }
+       printf("L%d:\n\t.data\nL%d:", brklab, swlab);
+       while (swp > sswp && swp > swtab) {
+               --swp;
+               printf("\t.long %d,L%d\n", swp->swval, swp->swlab);
+       }
+       printf("\t.long L%d,0\n", deflab);
+       printf("\t.text\n");
+       deflab = dl;
+       swp = sswp;
+}
+
+void
+statement(int d)
+{
+       int o, o1, o2;
+
+stmt:
+       if ((o = symbol()) == LBRACE) {
+               if (d)
+                       blkhed();
+               while (!eof) {
+                       if ((o = symbol()) == RBRACE)
+                               goto bend;
+                       pushsym(o);
+                       statement(0);
+               }
+               error("Missing '}'");
+       bend:
+               return;
+       } else {
+               pushsym(o);
+               if (d)
+                       blkhed();
+       }
+
+       switch (o = symbol()) {
+
+       case EOFC:
+               error("Unexpected EOF");
+
+       case SEMI:
+       case RBRACE:
+               return;
+
+       case KEYW:
+               switch (cval) {
+               case GOTO:
+                       if ((o = symbol()) != NAME)
+                               goto syntax;
+                       jump(bsym->offset);
+                       goto semi;
+
+               case RETURN:
+                       if (pushsym(symbol()) == LPARN)
+                               rcexpr(pexpr());
+                       retseq();
+                       goto semi;
+
+               case IF:
+                       cbranch(pexpr(), o1=isn++, 0);
+                       statement(0);
+                       if ((o = symbol()) == KEYW && cval == ELSE) {
+                               jump(o2 = isn++);
+                               label(o1);
+                               statement(0);
+                               label(o2);
+                               return;
+                       }
+                       pushsym(o);
+                       label(o1);
+                       return;
+
+               case WHILE:
+                       o1 = contlab;
+                       o2 = brklab;
+                       label(contlab = isn++);
+                       cbranch(pexpr(), brklab=isn++, 0);
+                       statement(0);
+                       jump(contlab);
+                       label(brklab);
+                       contlab = o1;
+                       brklab = o2;
+                       return;
+
+               case BREAK:
+                       if (brklab < 0)
+                               error("Nothing to break from");
+                       jump(brklab);
+                       goto semi;
+
+               /* Not part of B, but very easy to implement */
+/*
+               case CONTINUE:
+                       if (contlab < 0)
+                               error("Nothing to continue");
+                       jump(contlab);
+                       goto semi;
+*/
+
+               case SWITCH:
+                       o1 = brklab;
+                       brklab = isn++;
+                       rcexpr(pexpr());
+/*                     rcexpr(tree()); */
+                       pswitch();
+                       brklab = o1;
+                       return;
+
+               case CASE:
+                       if ((o = symbol()) != CON)
+                               goto syntax;
+                       if ((o = symbol()) != COLON)
+                               goto syntax;
+                       if (swp == NULL) {
+                               error("Case not in switch");
+                               goto stmt;
+                       }
+                       if (swp >= swtab+SWSIZ)
+                               error("Switch table overflow");
+                       else {
+                               swp->swlab = isn;
+                               (swp++)->swval = cval;
+                               label(isn++);
+                       }
+                       goto stmt;
+
+               case DEFAULT:
+                       if (swp == NULL)
+                               error("Default not in switch");
+                       if ((o = symbol()) != COLON)
+                               goto syntax;
+                       deflab = isn++;
+                       label(deflab);
+                       goto stmt;
+               }
+
+               error("Unknown keyword");
+               goto syntax;
+
+       case NAME:
+               if (peekc == ':') {
+                       peekc = 0;
+                       if (bsym->class) {
+                               error("Redefinition");
+                               goto stmt;
+                       }
+                       bsym->class = INTERN;
+                       bsym->offset = isn++;
+                       label(bsym->offset);
+                       goto stmt;
+               }
+       }
+       pushsym(o);
+       rcexpr(tree());
+       goto semi;
+
+semi:
+       if ((o = symbol()) != SEMI)
+               goto syntax;
+       return;
+
+syntax:
+       error("Statement syntax");
+       errflush(o);
+       goto stmt;
+}
+
+struct tnode *
+block(int op, int value, struct tnode *tr1, struct tnode *tr2)
+{
+       struct tnode t;
+       int n;
+       int *p, *ap;
+
+       p = space;
+       t.op = op;
+       t.value = value;
+       t.tr1 = tr1;
+       t.tr2 = tr2;
+       ap = (int*) &t;
+       n = (sizeof(struct tnode)+sizeof(int)-1) & ~sizeof(int);
+       if (space+n >= &ospace[OSSIZ]) {
+               error("Expression overflow 1");
+               exit(1);
+       }
+       while (n--)
+               *space++ = *ap++;
+       return (struct tnode *) p;
+}
+
+void
+chklval(struct tnode *p)
+{
+       if (p->op != NAME && p->op != STAR)
+               error("Lvalue required");
+}
+
+void
+build(int op)
+{
+       struct tnode *p1, *p2;
+       int dope;
+
+       /* a[i] -> *(a+i) */
+       if (op == LBRACK) {
+               build(PLUS);
+               op = STAR;
+       }
+       dope = opdope[op];
+       if (dope&01)
+               p2 = *--cp;
+       p1 = *--cp;
+       switch (op) {
+       case QUEST:
+               if (p2->op != COLON)
+                       error("Illegal conditional");
+               break;
+
+       case AMPER:
+               if (p1->op == STAR) {
+                       *cp++ = p1->tr1;
+                       return;
+               }
+               if (p1->op == NAME) {
+                       *cp++ = block(op,0,p1,NULL);
+                       return;
+               }
+               error("Illegal lvalue");
+       }
+       if (dope&02)
+               chklval(p1);
+       if (dope&01)
+               *cp++ = block(op,0,p1,p2);
+       else
+               *cp++ = block(op,0,p1,NULL);
+}
+
+struct tnode *
+tree(void)
+{
+       struct tnode *cmst[CMSIZ];
+       int opst[SSIZE], prst[SSIZE];
+       int *op, *pp;
+       int andflg;
+       int o, os;
+       int p, ps;
+
+       space = ospace;
+       op = opst;
+       pp = prst;
+       cp = cmst;
+       *op = SEOF;
+       *pp = 06;
+       andflg = 0;
+
+advanc:
+       switch (o=symbol()) {
+       case NAME:
+               if (pushsym(symbol()) == LPARN) {       /* function */
+                       bsym->class = EXTERN;
+               } else if (bsym->class == 0) {
+                       error("%s undefined", bsym->name);
+                       bsym->class = EXTERN;
+               }
+               *cp++ = block(NAME,0,(struct tnode *)bsym,NULL);
+               goto tand;
+
+       case STRING:
+               *cp++ = block(STRING,cval,NULL,NULL);
+               goto tand;
+
+       case CON:
+       caseCON:
+               *cp++ = block(CON,cval,NULL,NULL);
+               goto tand;
+
+tand:
+               if (cp >= &cmst[CMSIZ]) {
+                       error("Expression overflow 2");
+                       exit(1);
+               }
+               if (andflg)
+                       goto syntax;
+               andflg = 1;
+               goto advanc;
+
+       case DECBEF:
+       case INCBEF:
+               if (andflg)
+                       o += 2;
+               goto oponst;
+
+       case EXCLA:
+               if (andflg)
+                       goto syntax;
+               goto oponst;
+
+       case MINUS:
+               if (!andflg) {
+                       if (pushsym(symbol()) == CON) {
+                               symbol();
+                               cval = -cval;
+                               goto caseCON;
+                       }
+                       o = NEG;
+               }
+               andflg = 0;
+               goto oponst;
+
+       case AND:
+       case TIMES:
+               if (andflg)
+                       andflg = 0;
+               else
+                       if (o == AND)
+                               o = AMPER;
+                       else
+                               o = STAR;
+               goto oponst;
+
+       case LPARN:
+               if (andflg) {
+                       o = symbol();
+                       if (o == RPARN)
+                               o = MCALL;
+                       else {
+                               pushsym(o);
+                               o = CALL;
+                               andflg = 0;
+                       }
+               }
+               goto oponst;
+
+       case RPARN:
+       case RBRACK:
+               if (!andflg)
+                       goto syntax;
+               goto oponst;
+       }
+
+       if (!andflg)
+               goto syntax;
+       andflg = 0;
+
+oponst:
+       p = (opdope[o]>>9) & 077;
+opon1:
+       ps = *pp;
+       if (p > ps || (p == ps && (opdope[o]&0200))) {  /* right-assoc */
+               switch (o) {
+               case LPARN:
+               case LBRACK:
+               case CALL:
+                       p = 04;
+               }
+               if (op >= &opst[SSIZE]) {
+                       error("Expression overflow 3");
+                       exit(1);
+               }
+               *++op = o;
+               *++pp = p;
+               goto advanc;
+       }
+       --pp;
+       switch (os = *op--) {
+       case SEOF:
+               pushsym(o);
+               return(*--cp);
+
+       case CALL:
+               if (o != RPARN)
+                       goto syntax;
+               build(os);
+               goto advanc;
+
+       case MCALL:
+               *cp++ = NULL;
+               os = CALL;
+               goto fbuild;
+
+       case LPARN:
+               if (o != RPARN)
+                       goto syntax;
+               goto advanc;
+
+       case LBRACK:
+               if (o != RBRACK)
+                       goto syntax;
+               build(LBRACK);
+               goto advanc;
+
+       }
+fbuild:
+       build(os);
+       goto opon1;
+syntax:
+       error("Expression syntax");
+       errflush(o);
+       return NULL;
+}
+
+void
+error(char *s, ...)
+{
+       va_list args;
+
+       va_start(args, s);
+       nerror++;
+       fprintf(stderr, "%d: ", line);
+       vfprintf(stderr, s, args);
+       putc('\n', stderr);
+       va_end(args);
+}
+
+void
+errflush(int o)
+{
+       while (o > RBRACE)      /* ; { } */
+               o = symbol();
+       pushsym(o);
+}
+
+/*
+ *     000001  binary
+ *     000002  need lvalue
+ *     000004  relational
+ *     000010  assignment
+ *     000100  commutative
+ *     000200  right-assoc
+ *     0XX000  precedence
+ */
+int opdope[] = {
+       000000, /* EOFC */
+       000000, /* ; */
+       000000, /* { */
+       000000, /* } */
+       036000, /* [ */
+       002000, /* ] */
+       036000, /* ( */
+       002000, /* ) */
+       014201, /* : */
+       007001, /* , */
+       000000, /* 10 */
+       000000, /* 11 */
+       000000, /* 12 */
+       000000, /* 13 */
+       000000, /* 14 */
+       036001, /* mcall */
+       036001, /* call */
+       034202, /* ++a */
+       034202, /* a++ */
+       034202, /* --a */
+       034202, /* a-- */
+       034200, /* !un */
+       034200, /* -un */
+       034200, /* &un */
+       034200, /* *un */
+       014201, /* ? */
+       000000, /* 26 */
+       000000, /* 27 */
+       000000, /* 28 */
+       000000, /* 29 */
+       030101, /* + */
+       030001, /* - */
+       032001, /* % */
+       032101, /* * */
+       032001, /* / */
+       016101, /* | */
+       020101, /* & */
+       026001, /* << */
+       026001, /* >> */
+       022105, /* == */
+       022105, /* != */
+       024005, /* <= */
+       024005, /* < */
+       024005, /* >= */
+       024005, /* > */
+       000000, /* 45 */
+       000000, /* 46 */
+       000000, /* 47 */
+       000000, /* 48 */
+       012013, /* = */
+       012213, /* =+ */
+       012213, /* =- */
+       012213, /* =% */
+       012213, /* =* */
+       012213, /* =/ */
+       012213, /* =| */
+       012213, /* =& */
+       012213, /* =<< */
+       012213, /* =>> */
+       012213, /* === */
+       012213, /* =!= */
+       012213, /* =<= */
+       012213, /* =< */
+       012213, /* =>= */
+       012213, /* => */
+       000000, /* CON */
+       000000, /* STRING */
+       000000  /* NAME */
+};
+
+char ctab[128] = {
+       EOFC,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,
+       LETTER, SPACE,  NEWLN,  SPACE,  SPACE,  UNKN,   UNKN,   UNKN,
+       UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,
+       UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,   UNKN,
+       SPACE,  EXCLA,  DQUOTE, UNKN,   UNKN,   MOD,    AND,    SQUOTE,
+       LPARN,  RPARN,  TIMES,  PLUS,   COMMA,  MINUS,  UNKN,   DIVIDE,
+       DIGIT,  DIGIT,  DIGIT,  DIGIT,  DIGIT,  DIGIT,  DIGIT,  DIGIT,
+       DIGIT,  DIGIT,  COLON,  SEMI,   LESS,   ASSIGN, GREAT,  QUEST,
+       UNKN,   LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+       LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+       LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+       LETTER, LETTER, LETTER, LBRACK, UNKN,   RBRACK, UNKN,   LETTER,
+       UNKN,   LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+       LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+       LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+       LETTER, LETTER, LETTER, LBRACE, OR,     RBRACE, UNKN,   UNKN
+};
+
+/* debug function */
+void printtoken(int tok, FILE *out)
+{
+       static char *strtab[128];
+       strtab[0] = "EOFC";
+       strtab[1] = "SEMI";
+       strtab[2] = "LBRACE";
+       strtab[3] = "RBRACE";
+       strtab[4] = "LBRACK";
+       strtab[5] = "RBRACK";
+       strtab[6] = "LPARN";
+       strtab[7] = "RPARN";
+       strtab[8] = "COLON";
+       strtab[9] = "COMMA";
+
+       strtab[15] = "MCALL";
+       strtab[16] = "CALL";
+       strtab[17] = "DECBEF";
+       strtab[18] = "INCBEF";
+       strtab[19] = "DECAFT";
+       strtab[20] = "INCAFT";
+       strtab[21] = "EXCLA";
+       strtab[22] = "NEG";
+       strtab[23] = "AMPER";
+       strtab[24] = "STAR";
+       strtab[25] = "QUEST";
+
+       strtab[30] = "PLUS";
+       strtab[31] = "MINUS";
+       strtab[32] = "MOD";
+       strtab[33] = "TIMES";
+       strtab[34] = "DIVIDE";
+       strtab[35] = "OR";
+       strtab[36] = "AND";
+       strtab[37] = "LSHIFT";
+       strtab[38] = "RSHIFT";
+       strtab[39] = "EQUAL";
+       strtab[40] = "NEQUAL";
+       strtab[41] = "LESSEQ";
+       strtab[42] = "LESS";
+       strtab[43] = "GREATEQ";
+       strtab[44] = "GREAT";
+
+       strtab[49] = "ASSIGN";
+       strtab[50] = "ASPLUS";
+       strtab[51] = "ASMINUS";
+       strtab[52] = "ASMOD";
+       strtab[53] = "ASTIMES";
+       strtab[54] = "ASDIV";
+       strtab[55] = "ASOR";
+       strtab[56] = "ASAND";
+       strtab[57] = "ASLSH";
+       strtab[58] = "ASRSH";
+       strtab[59] = "ASEQUAL";
+       strtab[60] = "ASNEQL";
+       strtab[61] = "ASLEQ";
+       strtab[62] = "ASLESS";
+       strtab[63] = "ASGTQ";
+       strtab[64] = "ASGREAT";
+
+       strtab[65] = "CON";
+       strtab[66] = "STRING";
+       strtab[67] = "NAME";
+       strtab[68] = "KEYW";
+
+       strtab[127] = "UNKN";
+
+       if (tok == CON || tok == STRING) {
+               fprintf(out, "%s(%d) ", strtab[tok], cval);
+               return;
+       }
+       if (tok == NAME) {
+               fprintf(out, "%s(%s) ", strtab[tok], symbuf);
+               return;
+       }
+               
+       fprintf(out, "%s ", strtab[tok]);
+}
+
diff --git a/lang/b/distr/b1.c b/lang/b/distr/b1.c
new file mode 100644 (file)
index 0000000..6295810
--- /dev/null
@@ -0,0 +1,334 @@
+#include "b.h"
+
+/*
+ * Code generation (x86 assembly)
+ */
+
+void
+push(void)
+{
+       printf("\tpush\t%%eax\n");
+}
+
+void
+pop(char *s)
+{
+       printf("\tpop\t%%%s\n", s);
+}
+
+void
+binary(struct tnode *tr)
+{
+       rcexpr(tr->tr1);
+       push();
+       rcexpr(tr->tr2);
+}
+
+int
+pushargs(struct tnode *tr)
+{
+       int stk;
+
+       if (tr == NULL)
+               return 0;
+       if (tr->op == COMMA) {
+               rcexpr(tr->tr2);
+               push();
+               stk = pushargs(tr->tr1);
+               return stk+NCPW;
+       }
+       rcexpr(tr);
+       push();
+       return NCPW;
+}
+
+void
+lvalexp(struct tnode *tr)
+{
+       struct hshtab *bs;
+       char memloc[64];
+
+       switch (tr->op) {
+
+       case DECBEF:
+       case INCBEF:
+       case DECAFT:
+       case INCAFT:
+               if (tr->tr1->op == STAR) {
+                       rcexpr(tr->tr1->tr1);
+                       printf("\tmov\t%%eax,%%ebx\n");
+                       sprintf(memloc,"(,%%ebx,4)");
+               } else {        /* NAME, checked in "build" */
+                       bs = (struct hshtab *) tr->tr1->tr1;
+                       if (bs->class == EXTERN)
+                               sprintf(memloc,"_%s", bs->name);
+                       else if (bs->class == AUTO)
+                               sprintf(memloc,"%d(%%ebp)", bs->offset);
+                       else
+                               goto classerror;
+               }
+               if (tr->op == DECBEF || tr->op == INCBEF) {
+                       printf("\t%s\t%s\n", tr->op == DECBEF ? "decl" : "incl",
+                              memloc);
+                       printf("\tmov\t%s,%%eax\n", memloc);
+               } else {
+                       printf("\tmov\t%s,%%eax\n", memloc);
+                       printf("\t%s\t%s\n", tr->op == DECAFT ? "decl" : "incl",
+                              memloc);
+               }
+               return;
+
+       case ASSIGN:
+               rcexpr(tr->tr2);
+               if (tr->tr1->op == STAR) {
+                       push();
+                       rcexpr(tr->tr1->tr1);
+                       pop("ebx");
+                       printf("\tmov\t%%ebx,(,%%eax,4)\n");
+               } else {        /* NAME */
+                       bs = (struct hshtab *) tr->tr1->tr1;
+                       if (bs->class == EXTERN)
+                               printf("\tmov\t%%eax,_%s\n", bs->name);
+                       else if (bs->class == AUTO)
+                               printf("\tmov\t%%eax,%d(%%ebp)\n", bs->offset);
+                       else
+                               goto classerror;
+               }
+               return;
+
+       case ASPLUS:
+       case ASMINUS:
+       case ASMOD:
+       case ASTIMES:
+       case ASDIV:
+       case ASOR:
+       case ASAND:
+       case ASLSH:
+       case ASRSH:
+       case ASEQUAL:
+       case ASNEQL:
+       case ASLEQ:
+       case ASLESS:
+       case ASGTQ:
+       case ASGREAT:
+               tr->op -= ASPLUS-PLUS;
+               rcexpr(block(ASSIGN,0,tr->tr1,tr));
+               return;
+       }
+
+classerror:
+       error("Storage class");
+}
+
+void
+rcexpr(struct tnode *tr)
+{
+       int o1, o2;
+       int stk;
+       struct hshtab *bs;
+
+       if (tr == NULL)
+               return;
+
+       if (opdope[tr->op]&02) {
+               lvalexp(tr);
+               return;
+       }
+
+       switch (tr->op) {
+
+       case CON:
+               printf("\tmov\t$%d,%%eax\n", tr->value);
+               return;
+
+       case STRING:
+               printf("\tmov\t$L%d,%%eax\n", tr->value);
+               printf("\tshr\t$2,%%eax\n");
+               return;
+
+       case NAME:      /* only rvalue */
+               bs = (struct hshtab *) tr->tr1;
+               if (bs->class == EXTERN)
+                       printf("\tmov\t_%s,%%eax\n", bs->name);
+               else if (bs->class == AUTO)
+                       printf("\tmov\t%d(%%ebp),%%eax\n", bs->offset);
+               else
+                       goto classerror;
+               return;
+
+       case CALL:
+               stk = pushargs(tr->tr2);
+               rcexpr(tr->tr1);
+               printf("\tshl\t$2,%%eax\n");
+               printf("\tcall\t*%%eax\n");
+               if (stk)
+                       printf("\tadd\t$%d,%%esp\n",stk);
+               return;
+
+       case AMPER:
+               bs = (struct hshtab *) tr->tr1->tr1;
+               if (bs->class == EXTERN) {
+                       printf("\tmov\t$_%s,%%eax\n", bs->name);
+                       printf("\tshr\t$2,%%eax\n");
+               } else if (bs->class == AUTO) {
+                       printf("\tlea\t%d(%%ebp),%%eax\n", bs->offset);
+                       printf("\tshr\t$2,%%eax\n");
+               } else
+                       goto classerror;
+               return;
+
+       case STAR:      /* only rvalue */
+               rcexpr(tr->tr1);
+               printf("\tmov\t(,%%eax,4),%%eax\n");
+               return;
+
+       case PLUS:
+               binary(tr);
+               pop("ebx");
+               printf("\tadd\t%%ebx,%%eax\n");
+               return;
+
+       case MINUS:
+               binary(tr);
+               printf("\tmov\t%%eax,%%ebx\n");
+               pop("eax");
+               printf("\tsub\t%%ebx,%%eax\n");
+               return;
+
+       case TIMES:
+               binary(tr);
+               pop("ebx");
+               printf("\tmul\t%%ebx\n");
+               return;
+
+       case DIVIDE:
+               binary(tr);
+               printf("\tmov\t%%eax,%%ebx\n");
+               pop("eax");
+               printf("\txor\t%%edx,%%edx\n");
+               printf("\tdiv\t%%ebx\n");
+               return;
+
+       case MOD:
+               binary(tr);
+               printf("\tmov\t%%eax,%%ebx\n");
+               pop("eax");
+               printf("\txor\t%%edx,%%edx\n");
+               printf("\tdiv\t%%ebx\n");
+               printf("\tmov\t%%edx,%%eax\n");
+               return;
+
+       case AND:
+               binary(tr);
+               pop("ebx");
+               printf("\tand\t%%ebx,%%eax\n");
+               return;
+
+       case OR:
+               binary(tr);
+               pop("ebx");
+               printf("\tor\t%%ebx,%%eax\n");
+               return;
+
+       case LSHIFT:
+               binary(tr);
+               printf("\tmov\t%%eax,%%ecx\n");
+               pop("eax");
+               printf("\tshl\t%%cl,%%eax\n");
+               return;
+
+       case RSHIFT:
+               binary(tr);
+               printf("\tmov\t%%eax,%%ecx\n");
+               pop("eax");
+               printf("\tshr\t%%cl,%%eax\n");
+               return;
+
+       case EQUAL:
+       case NEQUAL:
+       case LESS:
+       case LESSEQ:
+       case GREAT:
+       case GREATEQ:
+               binary(tr);
+               pop("ebx");
+               printf("\tcmp\t%%eax,%%ebx\n");
+               switch (tr->op) {
+               case EQUAL:
+                       printf("\tsete\t%%al\n");
+                       break;
+               case NEQUAL:
+                       printf("\tsetne\t%%al\n");
+                       break;
+               case LESS:
+                       printf("\tsetl\t%%al\n");
+                       break;
+               case LESSEQ:
+                       printf("\tsetle\t%%al\n");
+                       break;
+               case GREAT:
+                       printf("\tsetg\t%%al\n");
+                       break;
+               case GREATEQ:
+                       printf("\tsetge\t%%al\n");
+                       break;
+               }
+               printf("\tmovzb\t%%al,%%eax\n");
+               return;
+
+       case EXCLA:
+               rcexpr(tr->tr1);
+               printf("\ttest\t%%eax,%%eax\n");
+               printf("\tsete\t%%al\n");
+               printf("\tmovzb\t%%al,%%eax\n");
+               return;
+
+       case NEG:
+               rcexpr(tr->tr1);
+               printf("\tneg\t%%eax\n");
+               return;
+
+       case QUEST:
+               cbranch(tr->tr1, o1=isn++, 0);
+               rcexpr(tr->tr2->tr1);
+               jump(o2 = isn++);
+               label(o1);
+               rcexpr(tr->tr2->tr2);
+               label(o2);
+               return;
+
+       default:
+               error("Can't print tree (op: %d)", tr->op);
+       }
+
+classerror:
+       error("Storage class");
+}
+
+/* Prints the tree in RPN, for debugging */
+/*
+void
+rcexpr(struct tnode *tr)
+{
+       struct hshtab *bs;
+
+       if (tr == NULL)
+               printf("(NULL) ");
+       else if (tr->op == CON)
+               printf("%d ", tr->value);
+       else if (tr->op == STRING)
+               printf("s(L%d) ", tr->value);
+       else if (tr->op == NAME) {
+               bs = (struct hshtab *)tr->tr1;
+               if (bs->class == AUTO)
+                       printf("%s(%d) ", bs->name, bs->offset);
+               else
+                       printf("%s ", bs->name);
+       } else {
+               rcexpr(tr->tr1);
+               if (opdope[tr->op]&01)
+                       rcexpr(tr->tr2);
+               printtoken(tr->op, stdout);
+       }
+}
+*/
diff --git a/lang/b/distr/brt.s b/lang/b/distr/brt.s
new file mode 100644 (file)
index 0000000..18f4fb3
--- /dev/null
@@ -0,0 +1,151 @@
+       .globl _argv
+       .data
+       .align 4
+       .comm   argstr,256,4
+_argv: .long   0
+
+       .text
+       .globl start
+start:
+       # clear bss (could be done better)
+       # is it actually necessary?
+       mov     $__bss,%eax
+1:
+       movb    $0,(%eax)
+       inc     %eax
+       cmp     $__ebss,%eax
+       jbe     1b
+
+       # copy command line args (can't use them directly, not aligned)
+       mov     %esp,%eax
+       shr     $2,%eax
+       mov     %eax,_argv
+       mov     (%esp),%ecx     # number of arguments
+       mov     $argstr,%edi
+1:
+       mov     (%esp,%ecx,4),%esi
+       mov     %edi,%eax
+       shr     $2,%eax
+       mov     %eax,(%esp,%ecx,4)
+       call    cpystr
+       loop    1b
+
+       call    bsymbs
+       mov     _main,%eax
+       shl     $2,%eax
+       call    *%eax
+       mov     %eax,%ebx
+       mov     $1,%eax
+       int     $0x80
+
+# copy string from esi to edi and convert '\0' to B's '*e', align edi
+cpystr:
+       mov     (%esi),%al
+       test    %al,%al
+       jz      1f
+       mov     %al,(%edi)
+       inc     %edi
+       inc     %esi
+       jmp     cpystr
+1:
+       movb    $04,(%edi)
+       inc     %edi
+       add     $3,%edi
+       and     $~3,%edi
+       ret
+
+# shift addresses filled in by the linker 2 bits to the right
+# so B only ever sees word addresses
+bsymbs:
+       mov     $__bsymb,%eax
+1:
+       cmp     $__ebsymb,%eax
+       jge     1f
+       mov     (%eax),%ebx
+       mov     (%ebx),%ecx
+       shr     $2,%ecx
+       mov     %ecx,(%ebx)
+       add     $4,%eax
+       jmp     1b
+1:
+       ret
+
+       .globl retrn
+retrn:
+       mov     %ebp,%esp
+       pop     %ebp
+       ret
+
+# handle switch table:
+# eax has the value, ebx the address of the switch table
+       .globl bswitch
+bswitch:
+       xor     %ecx,%ecx
+1:
+       mov     (%ebx,%ecx,8),%edx
+       mov     4(%ebx,%ecx,8),%edi
+       test    %edi,%edi
+       jz      1f              # default (last in table)
+       cmp     %eax,%edx
+       je      2f
+       inc     %ecx
+       jmp     1b
+1:
+       jmp     *%edx
+2:
+       jmp     *%edi
+
+#
+# Library functions in assembly
+#
+       .globl _exit
+       .data
+       .align 4
+       .section .bsymb; .long _exit; .data
+_exit: .long   1f
+       .text
+       .align 4
+1:     mov     $1,%eax
+       mov     $0,%ebx
+       int     $0x80
+
+       .globl _write
+       .data
+       .align 4
+       .section .bsymb; .long _write; .data
+_write:        .long   1f
+       .text
+       .align 4
+1:     mov     4(%esp),%ebx
+       mov     8(%esp),%ecx
+       shl     $2,%ecx
+       mov     12(%esp),%edx
+       mov     $4,%eax
+       int     $0x80
+       ret
+
+       .globl _read
+       .data
+       .align 4
+       .section .bsymb; .long _read; .data
+_read: .long   1f
+       .text
+       .align 4
+1:     mov     4(%esp),%ebx
+       mov     8(%esp),%ecx
+       shl     $2,%ecx
+       mov     12(%esp),%edx
+       mov     $3,%eax
+       int     $0x80
+       ret
+
+       .globl  _inv
+       .data
+       .align 4
+       .section .bsymb; .long _inv; .data
+_inv:  .long   1f
+       .text
+       .align 4
+1:     mov     4(%esp),%eax
+       not     %eax
+       ret
diff --git a/lang/b/distr/examples/1_var.b b/lang/b/distr/examples/1_var.b
new file mode 100644 (file)
index 0000000..aa31544
--- /dev/null
@@ -0,0 +1,7 @@
+main() {
+       auto a, b, c, sum;
+
+       a = 1; b = 2; c = 3;
+       sum = a+b+c;
+       putnumb(sum);
+}
diff --git a/lang/b/distr/examples/2_ext.b b/lang/b/distr/examples/2_ext.b
new file mode 100644 (file)
index 0000000..a8deac6
--- /dev/null
@@ -0,0 +1,9 @@
+main() {
+       extrn a, b, c;
+       putchar(a); putchar(b); putchar(c); putchar('!*n');
+}
+
+a 'hell';
+b 'o, w';
+c 'orld';
+
diff --git a/lang/b/distr/examples/3_fun.b b/lang/b/distr/examples/3_fun.b
new file mode 100644 (file)
index 0000000..16d7409
--- /dev/null
@@ -0,0 +1,13 @@
+main() {
+       extrn a, b, c, d;
+       put2char(a,b);
+       put2char(c,d);
+}
+
+put2char(x,y) {
+putchar(x);
+putchar(y);
+}
+
+a 'hell'; b 'o, w'; c 'orld'; d '!*n';
+
diff --git a/lang/b/distr/examples/4_goto.b b/lang/b/distr/examples/4_goto.b
new file mode 100644 (file)
index 0000000..2d5256a
--- /dev/null
@@ -0,0 +1,7 @@
+main() {
+       auto c;
+read:
+       c= getchar();
+       putchar(c);
+       if(c != '*n')   goto read;
+}
diff --git a/lang/b/distr/examples/5_while.b b/lang/b/distr/examples/5_while.b
new file mode 100644 (file)
index 0000000..13baa0c
--- /dev/null
@@ -0,0 +1,10 @@
+main() {
+       auto c;
+       while (1) {
+               while ( (c=getchar()) != ' ')
+                       if (putchar(c) == '*n') exit();
+               putchar( '*n' );
+               while ( (c=getchar()) == ' ');  /* skip blanks */
+               if (putchar(c)=='*n') exit();
+               }
+}
diff --git a/lang/b/distr/examples/printargs.b b/lang/b/distr/examples/printargs.b
new file mode 100644 (file)
index 0000000..4449320
--- /dev/null
@@ -0,0 +1,10 @@
+main() {
+       extrn argv;
+       auto i;
+
+       i = 1;
+       printf("%d args:*n", argv[0]);
+       while (i <= argv[0])
+               printf("%s*n", argv[i++]);
+       return(0);
+}
diff --git a/lang/b/distr/lib.b b/lang/b/distr/lib.b
new file mode 100644 (file)
index 0000000..9e274eb
--- /dev/null
@@ -0,0 +1,111 @@
+char(s, n)
+       return((s[n/4]>>8*(n%4))&0377); /* s[n/4] */
+
+lchar(s, n, char) {
+       auto i;
+       i = 8*(n%4);
+       char = (char&0377)<<i;
+       i = inv(0377<<i);
+       s[(n/4)*4] = s[n/4]&i | char;
+}
+
+putchar(char) {
+       auto c, i;
+
+       c = char;
+       i = 4;
+       while ((c&0377) != '*e' & (c&0377) != '*0' & i != 0) {
+               i--;
+               c =>> 8;
+       }
+       write(1, &char, 4-i);
+       return(char);
+}
+
+getchar() {
+       auto char;
+
+       char = 0;
+       read(1, &char, 1);
+       return(char);
+}
+
+printn(n,b) {
+       extrn putchar;
+       auto a;
+
+       if (a = n/b)
+               printn(a, b);
+       putchar(char("0123456789ABCDEF", n%b));
+}
+
+putnumb(n) {
+       printn(n,10);
+       putchar('*n');
+}
+
+putstr(s) {
+       auto c, i;
+
+       i = 0;
+       while ((c = char(s,i++)) != '*e')
+               putchar(c);
+}
+
+getstr(s) {
+       auto c, i;
+
+       while ((c = getchar()) != '*n')
+               lchar(s,i++,c);
+       lchar(s,i,'*e');
+       return(s);
+}
+
+printf(fmt, x1,x2,x3,x4,x5,x6,x7,x8,x9) {
+       extrn printn, char, putchar;
+       auto adx, x, c, i, j;
+
+       i = 0;
+       adx = &x1;
+loop:
+       while((c=char(fmt,i++)) != '%') {
+               if(c == '*e')
+                       return;
+               putchar(c);
+       }
+       x = *adx++;
+       switch (c = char(fmt,i++)) {
+
+       case 'd':
+       case 'o':
+               if(x < 0) {
+                       x = -x;
+                       putchar('-');
+               }
+               printn(x, c=='o'?8:10);
+               goto loop;
+
+       case 'x':
+               if(x < 0) {
+                       x = -x;
+                       putchar('-');
+               }
+               printn(x, 16);
+               goto loop;
+
+       case 'c':
+               putchar(x);
+               goto loop;
+
+       case 's':
+               j = 0;
+               while((c=char(x,j++)) != '*e')
+                       putchar(c);
+               goto loop;
+       }
+       putchar('%');
+       i--;
+       adx--;
+       goto loop;
+}
+
diff --git a/lang/b/distr/link.ld b/lang/b/distr/link.ld
new file mode 100644 (file)
index 0000000..fd0fb71
--- /dev/null
@@ -0,0 +1,21 @@
+OUTPUT_FORMAT("elf32-i386")
+OUTPUT_ARCH(i386)
+
+ENTRY(start)
+
+SECTIONS
+{
+       . = 0x400000;
+       .text : { *(.text) }
+       . = 0x8000000;
+       .data : {
+               *(.data)
+               __bsymb = .;
+               *(.bsymb)
+               __ebsymb = .;
+       }
+       . = ALIGN(16);
+       __bss = .;
+       .bss : { *(.bss) }
+       __ebss = .;
+}