1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use crate::{
    ast::Identifier,
    runtime::{
        self,
        EvalResult,
        Parser,
    },
    CallContext,
    Exception,
    Heap,
    HeapNode,
    HostFn,
    Interpretable,
    Interpreted,
    JSRef,
    JSResult,
    Program,
};
use serde_json::json;

fn esprima_eval(call: CallContext, heap: &mut Heap) -> JSResult<Interpreted> {
    let code = call.arg_value(0, heap)?.stringify(heap)?;

    let esprima_ref = (heap.get(Heap::GLOBAL).get_own_value("esprima"))
        .ok_or_else(|| Exception::ReferenceNotFound(Identifier::from("esprima")))?
        .to_ref()?;
    let parse_ref = (heap.get(esprima_ref).get_own_value("parse"))
        .ok_or_else(|| {
            Exception::TypeErrorGetProperty(Interpreted::from(esprima_ref), "parse".into())
        })?
        .to_ref()?;

    let parser = EsprimaParser {
        loc: true,
        object: esprima_ref,
        esparse: parse_ref,
        locflag: Heap::NULL,
    };
    let program = parser.parse(&code, heap)?;
    program.interpret(heap)
}

/// [`EsprimaParser`] is an experimental parser that runs Esprima in sljs
#[derive(Debug)]
pub struct EsprimaParser {
    loc: bool,
    object: JSRef,
    esparse: JSRef,
    locflag: JSRef,
}

impl EsprimaParser {
    const ESPRIMA: &'static str = include_str!("../../tmp/esprima.json");

    pub fn new() -> Box<Self> {
        Box::new(Self {
            loc: true,
            object: Heap::NULL,
            esparse: Heap::NULL,
            locflag: Heap::NULL,
        })
    }

    pub fn without_location(mut self: Box<Self>) -> Box<Self> {
        self.loc = false;
        self
    }
}

impl runtime::Parser for EsprimaParser {
    fn load(&mut self, heap: &mut Heap) -> EvalResult<()> {
        let esprima_json = serde_json::from_str::<serde_json::Value>(Self::ESPRIMA)?;

        let esprima = Program::parse_from(&esprima_json).map_err(Exception::SyntaxTreeError)?;

        esprima.interpret(heap)?;

        let object: JSRef = heap.lookup_path(&["esprima"])?.to_ref(heap)?;
        let esparse = (heap.get(object))
            .get_own_value("parse")
            .ok_or_else(|| Exception::ReferenceNotFound(Identifier::from("esprima.parse")))?
            .to_ref()?;

        let mut locflag = JSRef::NULL;
        if self.loc {
            let locjson = json!({ "loc": true });
            locflag = heap.object_from_json(&locjson).to_ref()?;
        }

        self.object = object;
        self.esparse = esparse;
        self.locflag = locflag;
        Ok(())
    }

    fn parse(&self, input: &str, heap: &mut Heap) -> EvalResult<Program> {
        let mut arguments = vec![Interpreted::from(input)];
        if self.locflag != Heap::NULL {
            arguments.push(Interpreted::from(self.locflag));
        }
        let estree: Interpreted = heap.execute(
            self.esparse,
            CallContext::from(arguments)
                .with_this(self.object)
                .with_name("parse".into()),
        )?;
        let node = estree.to_ref(heap)?;

        let program =
            HeapNode::with(heap, node, Program::parse_from).map_err(Exception::SyntaxTreeError)?;
        Ok(program)
    }

    fn eval_func(&self) -> HostFn {
        esprima_eval
    }
}