~tim/scheme-vm

350ad6234a136103d34340efb0d0d349a988770c — Tim Morgan 3 years ago 046b621
Store line and column number for better errors
7 files changed, 23 insertions(+), 11 deletions(-)

M parser.rb
M program.rb
M spec/program_spec.rb
M src/atom.rs
M src/lib.rs
M src/lisp.rustpeg
M vm/atom.rb
M parser.rb => parser.rb +1 -1
@@ 16,7 16,7 @@ class Parser

  def initialize(code = nil, filename: nil)
    @code = code
    @filename = filename
    @filename = filename || ''
  end

  def parse

M program.rb => program.rb +1 -1
@@ 46,7 46,7 @@ class Program
  end

  def error_details_to_s(e)
    return '' unless e.filename && @compiler.source[e.filename]
    return '' unless e.filename && e.filename != '' && @compiler.source[e.filename]
    lines_range = (e.line - 2)..(e.line - 1)
    code = @compiler.source[e.filename].split("\n")[lines_range].map { |l| "  #{l}" }.join("\n")
    line = "#{e.filename}##{e.line}"

M spec/program_spec.rb => spec/program_spec.rb +1 -1
@@ 215,7 215,7 @@ describe Program do

    context 'exception in macro in another file' do
      let(:code) do
        '(include "./fixtures/bad-macro")' \
        '(include "./fixtures/bad-macro") ' \
        '(bad-macro)'
      end


M src/atom.rs => src/atom.rs +8 -3
@@ 1,9 1,14 @@
use rb;
use rb::{Value, RB_NIL};

pub fn atom(name: &str) -> Value {
    let name_rbstr = rb::str_new(&name.to_string());
pub fn atom(name: &str, filename: &str, offset: usize, input: &str) -> Value {
    let line = int2rbnum!(&input[..offset].matches("\n").count() + 1);
    let newline_index = *&input[..offset].match_indices("\n").last().unwrap_or((0, "\n")).0;
    let column = int2rbnum!(offset - newline_index);
    let name_str = rb::str_new(&name.to_string());
    let filename_str = rb::str_new(&filename.to_string());
    let offset_num = int2rbnum!(offset);
    let vm_class = rb::const_get("VM", &RB_NIL);
    let atom_class = rb::const_get("Atom", &vm_class);
    rb::class_new_instance(&atom_class, vec![name_rbstr])
    rb::class_new_instance(&atom_class, vec![name_str, filename_str, offset_num, line, column])
}

M src/lib.rs => src/lib.rs +5 -1
@@ 15,8 15,9 @@ mod lisp {

fn parse_native(rself: Value) -> Value {
    let program_str = rbstr2str!(&rb::ivar_get(&rself, "@code"));
    let filename = rbstr2str!(&rb::ivar_get(&rself, "@filename"));
    rb::gc_disable();
    match lisp::program(&program_str) {
    match lisp::program(&program_str, &filename) {
        Ok(ast) => {
            rb::gc_enable();
            ast


@@ 26,8 27,11 @@ fn parse_native(rself: Value) -> Value {
            //let expected = rb::vec2rbarr(
                //err.expected.iter().cloned().map(|e| rb::str_new(&e.to_string())).collect()
            //);
            println!("{}", err.line);
            println!("{}", err.column);
            println!("{:?}", err.expected);
            println!("{:?}", &program_str);
            println!("{:?}", &program_str[err.column..]);
            let c_parser = rb::const_get("Parser", &RB_NIL);
            let c_parse_error = rb::const_get("ParseError", &c_parser);
            let line = int2rbnum!(err.line);

M src/lisp.rustpeg => src/lisp.rustpeg +3 -1
@@ 1,3 1,5 @@
#![arguments(filename: &str)]

use rb;
use rb::Value;
use atom::atom;


@@ 16,7 18,7 @@ delimited_identifier -> &'input str
	= "|" i:$([^|]+) "|" {i}

simple_atom -> Value
	= a:(delimited_identifier / $([^\(\) \t\n\[\]\{\}\|"]+)) { atom(&a) }
	= p:#position a:(delimited_identifier / $([^\(\) \t\n\[\]\{\}\|"]+)) { atom(&a, &filename, p, __input) }

quoted_atom -> Value
	= q:quote a:simple_atom { rb::vec2rbarr(vec![q, a]) }

M vm/atom.rb => vm/atom.rb +4 -3
@@ 2,11 2,12 @@ require 'parslet'

class VM
  class Atom < String
    attr_reader :filename, :line, :column
    attr_reader :filename, :offset, :line, :column

    def initialize(name, filename: nil, line: nil, column: nil)
    def initialize(name, filename = nil, offset = nil, line = nil, column = nil)
      super(name.to_s)
      @filename = filename
      @offset = offset
      @line = line
      @column = column
    end


@@ 22,7 23,7 @@ class VM
    end

    def copy_and_rename(name)
      self.class.new(name, filename: filename, line: line, column: column)
      self.class.new(name, filename, offset, line, column)
    end

    def sub(*_args)