basic-lang 0.2.0

The BASIC programming language as it was in 1978.
Documentation
use super::{Op, Program, Stack, Val};
use crate::error;
use crate::lang::ast::{self, AcceptVisitor};
use crate::lang::{Column, Error, LineNumber};
use std::convert::TryFrom;

type Result<T> = std::result::Result<T, Error>;

pub fn compile(program: &mut Program, ast: &[ast::Statement]) {
    Visitor::compile(program, ast)
}

struct Visitor<'a> {
    prog: &'a mut Program,
    comp: Compiler,
}

impl<'a> Visitor<'a> {
    fn compile(program: &mut Program, ast: &[ast::Statement]) {
        let mut this = Visitor {
            prog: program,
            comp: Compiler::new(),
        };
        for statement in ast {
            statement.accept(&mut this);
        }
    }
}

impl<'a> ast::Visitor for Visitor<'a> {
    fn visit_statement(&mut self, statement: &ast::Statement) {
        match self.comp.statement(statement, self.prog) {
            Ok(_col) => {
                debug_assert_eq!(0, self.comp.ident.len());
                debug_assert_eq!(0, self.comp.expr.len());
            }
            Err(e) => self.prog.error(e),
        };
    }
    fn visit_ident(&mut self, ident: &ast::Ident) {
        use ast::Ident;
        let ident = match ident {
            Ident::Plain(col, s)
            | Ident::String(col, s)
            | Ident::Single(col, s)
            | Ident::Double(col, s)
            | Ident::Integer(col, s) => (col.clone(), s.clone()),
        };
        if let Some(error) = self.comp.ident.push(ident).err() {
            self.prog.error(error)
        }
    }
    fn visit_expression(&mut self, expression: &ast::Expression) {
        let mut prog: Stack<Op> = Stack::new("COMPILED EXPRESSION TOO LARGE");
        match self.comp.expression(&mut prog, expression) {
            Ok(col) => {
                if let Some(error) = self.comp.expr.push((col.clone(), prog)).err() {
                    self.prog.error(error.in_column(&col))
                }
            }
            Err(e) => self.prog.error(e),
        };
    }
}

struct Compiler {
    ident: Stack<(Column, String)>,
    expr: Stack<(Column, Stack<Op>)>,
}

impl Compiler {
    fn new() -> Compiler {
        Compiler {
            ident: Stack::new("COMPILER IDENT OVERFLOW"),
            expr: Stack::new("COMPILER EXPRESSION OVERFLOW"),
        }
    }

    fn expression(&mut self, prog: &mut Stack<Op>, expr: &ast::Expression) -> Result<Column> {
        fn binary_expression(this: &mut Compiler, prog: &mut Stack<Op>, op: Op) -> Result<Column> {
            let (col_rhs, mut rhs) = this.expr.pop()?;
            let (col_lhs, mut lhs) = this.expr.pop()?;
            prog.append(&mut lhs)?;
            prog.append(&mut rhs)?;
            prog.push(op)?;
            Ok(col_lhs.start..col_rhs.end)
        }
        fn op(prog: &mut Stack<Op>, col: &Column, op: Op) -> Result<Column> {
            prog.push(op)?;
            Ok(col.clone())
        }
        use ast::Expression;
        match expr {
            Expression::Single(col, val) => op(prog, col, Op::Literal(Val::Single(*val))),
            Expression::Double(col, val) => op(prog, col, Op::Literal(Val::Double(*val))),
            Expression::Integer(col, val) => op(prog, col, Op::Literal(Val::Integer(*val))),
            Expression::String(col, val) => op(prog, col, Op::Literal(Val::String(val.clone()))),
            Expression::Char(col, val) => op(prog, col, Op::Literal(Val::Char(*val))),
            Expression::Var(col, _) => {
                let (_, ident) = self.ident.pop()?;
                op(prog, col, Op::Push(ident))
            }
            Expression::Function(col, ..) => {
                Err(error!(InternalError, ..col; "FUNCTIONS NOT YET COMPILING; PANIC"))
            }
            Expression::Negation(col, ..) => {
                let (expr_col, mut ops) = self.expr.pop()?;
                prog.append(&mut ops)?;
                prog.push(Op::Neg)?;
                Ok(col.start..expr_col.end)
            }
            Expression::Exponentiation(..) => binary_expression(self, prog, Op::Exp),
            Expression::Multiply(..) => binary_expression(self, prog, Op::Mul),
            Expression::Divide(..) => binary_expression(self, prog, Op::Div),
            Expression::DivideInt(..) => binary_expression(self, prog, Op::DivInt),
            Expression::Modulus(..) => binary_expression(self, prog, Op::Mod),
            Expression::Add(..) => binary_expression(self, prog, Op::Add),
            Expression::Subtract(..) => binary_expression(self, prog, Op::Sub),
            Expression::Equal(..) => binary_expression(self, prog, Op::Eq),
            Expression::NotEqual(..) => binary_expression(self, prog, Op::NotEq),
            Expression::Less(..) => binary_expression(self, prog, Op::Lt),
            Expression::LessEqual(..) => binary_expression(self, prog, Op::LtEq),
            Expression::Greater(..) => binary_expression(self, prog, Op::Gt),
            Expression::GreaterEqual(..) => binary_expression(self, prog, Op::GtEq),
            Expression::Not(..) => binary_expression(self, prog, Op::Not),
            Expression::And(..) => binary_expression(self, prog, Op::And),
            Expression::Or(..) => binary_expression(self, prog, Op::Or),
            Expression::Xor(..) => binary_expression(self, prog, Op::Xor),
            Expression::Imp(..) => binary_expression(self, prog, Op::Imp),
            Expression::Eqv(..) => binary_expression(self, prog, Op::Eqv),
        }
    }

    fn statement(&mut self, statement: &ast::Statement, prog: &mut Program) -> Result<Column> {
        use ast::Statement;
        match statement {
            Statement::Clear(col, ..) => self.r#clear(prog, col),
            Statement::End(col, ..) => self.r#end(prog, col),
            Statement::For(col, ..) => self.r#for(prog, col),
            Statement::Goto(col, ..) => self.r#goto(prog, col),
            Statement::Let(col, ..) => self.r#let(prog, col),
            Statement::List(col, ..) => self.r#list(prog, col),
            Statement::Next(col, ..) => self.r#next(prog, col),
            Statement::Print(col, ..) => self.r#print(prog, col),
            Statement::Run(col, ..) => self.r#run(prog, col),
        }
    }

    fn expr_pop_line_number(&mut self) -> Result<(Column, LineNumber)> {
        let (sub_col, mut ops) = self.expr.pop()?;
        if ops.len() == 1 {
            if let Op::Literal(val) = ops.pop()? {
                return Ok((sub_col, LineNumber::try_from(val)?));
            }
        }
        Err(error!(UndefinedLine, ..&sub_col; "INVALID LINE NUMBER"))
    }

    fn r#clear(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        prog.push(Op::Clear)?;
        Ok(col.clone())
    }

    fn r#end(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        prog.push(Op::End)?;
        Ok(col.clone())
    }

    fn r#for(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        let (step_col, mut step_ops) = self.expr.pop()?;
        let (_to_col, mut to_ops) = self.expr.pop()?;
        let (_from_col, mut from_ops) = self.expr.pop()?;
        let (_ident_col, var_name) = self.ident.pop()?;
        prog.append(&mut step_ops)?;
        prog.append(&mut to_ops)?;
        prog.append(&mut from_ops)?;
        prog.push(Op::Pop(var_name.clone()))?;
        prog.push(Op::Literal(Val::String(var_name.clone())))?;
        prog.push(Op::Literal(Val::Integer(0)))?;
        prog.push_for(col.start..step_col.end, var_name)?;
        Ok(col.start..step_col.end)
    }

    fn r#goto(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        let (sub_col, line_number) = self.expr_pop_line_number()?;
        let full_col = col.start..sub_col.end;
        prog.push_goto(sub_col, line_number)?;
        Ok(full_col)
    }

    fn r#let(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        let (sub_col, mut ops) = self.expr.pop()?;
        prog.append(&mut ops)?;
        let (_col, ident) = self.ident.pop()?;
        prog.push(Op::Pop(ident))?;
        Ok(col.start..sub_col.end)
    }

    fn r#list(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        let (col_to, ln_to) = self.expr_pop_line_number()?;
        let (_col_from, ln_from) = self.expr_pop_line_number()?;
        prog.push(Op::Literal(Val::try_from(ln_from)?))?;
        prog.push(Op::Literal(Val::try_from(ln_to)?))?;
        prog.push(Op::List)?;
        Ok(col.start..col_to.end)
    }

    fn r#next(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        let mut full_col = col.clone();
        if self.ident.is_empty() {
            prog.push_next(col.clone(), "".to_string())?;
        } else {
            for (col, var_name) in self.ident.drain(..) {
                full_col.end = col.end;
                prog.push_next(col, var_name)?;
            }
        }
        Ok(full_col)
    }

    fn r#print(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        let len = self.expr.len();
        let mut exprs = self.expr.pop_n(len)?;
        let mut col = col.clone();
        for (sub_col, mut expr) in exprs.drain(..) {
            prog.append(&mut expr)?;
            col.end = sub_col.end;
        }
        match i16::try_from(len) {
            Ok(len) => prog.push(Op::Literal(Val::Integer(len)))?,
            Err(_) => return Err(error!(Overflow, ..&col; "TOO MANY ELEMENTS")),
        };
        prog.push(Op::Print)?;
        Ok(col)
    }

    fn r#run(&mut self, prog: &mut Program, col: &Column) -> Result<Column> {
        let mut line_number: LineNumber = None;
        let (sub_col, mut ops) = self.expr.pop()?;
        if ops.len() == 1 {
            if let Op::Literal(val) = ops.pop()? {
                if let Ok(ln) = LineNumber::try_from(val) {
                    line_number = ln;
                }
            }
        }
        let full_col = col.start..sub_col.end;
        prog.push_run(sub_col, line_number)?;
        Ok(full_col)
    }
}