1use js_sys::Function;
4use wasm_bindgen::prelude::*;
5
6extern crate console_error_panic_hook;
7
8use crate::solver::Solver;
9
10use super::error::Error;
11use super::parser::{
12 ParserOutput,
13 lexer::{Tok, Token, tokenize},
14};
15use super::typechecker::check;
16
17fn serialize_token(t: &Token) -> Option<JsValue> {
18 let d = match &t.data {
19 Tok::Identifier(_) => 0,
20 Tok::Op(_) => 1,
21 Tok::AssignOp(_) => 1,
22 Tok::Float(_) => 2,
23 Tok::Int(_) => 2,
24 Tok::Keyword(_) => 3,
25 Tok::Error(_) => 4,
26 Tok::IndentError(_) => 4,
27 Tok::Comment => 5,
28 _ => return None,
29 };
30
31 let obj = js_sys::Object::new();
32 js_sys::Reflect::set(&obj, &JsValue::from("line"), &JsValue::from(t.line)).ok()?;
33 js_sys::Reflect::set(&obj, &JsValue::from("start"), &JsValue::from(t.start)).ok()?;
34 js_sys::Reflect::set(&obj, &JsValue::from("end"), &JsValue::from(t.end)).ok()?;
35 js_sys::Reflect::set(&obj, &JsValue::from("data"), &JsValue::from(d)).ok()?;
36
37 Some(obj.into())
38}
39
40fn serialize_error(e: &Error, tokens: &[Token]) -> Option<JsValue> {
41 let obj = js_sys::Object::new();
42 js_sys::Reflect::set(
43 &obj,
44 &JsValue::from("type"),
45 &JsValue::from(e.data.error_type.to_string()),
46 )
47 .ok()?;
48 js_sys::Reflect::set(
49 &obj,
50 &JsValue::from("message"),
51 &JsValue::from(e.data.message.clone()),
52 )
53 .ok()?;
54 js_sys::Reflect::set(
55 &obj,
56 &JsValue::from("start_line"),
57 &JsValue::from(tokens[e.start].line),
58 )
59 .ok()?;
60 js_sys::Reflect::set(
61 &obj,
62 &JsValue::from("start_column"),
63 &JsValue::from(tokens[e.start].start),
64 )
65 .ok()?;
66 js_sys::Reflect::set(
67 &obj,
68 &JsValue::from("end_line"),
69 &JsValue::from(tokens[if e.end == 0 { e.end } else { e.end - 1 }].line),
70 )
71 .ok()?;
72 js_sys::Reflect::set(
73 &obj,
74 &JsValue::from("end_column"),
75 &JsValue::from(tokens[if e.end == 0 { e.end } else { e.end - 1 }].end),
76 )
77 .ok()?;
78 Some(obj.into())
79}
80
81fn serialize_type_tokens(c: &ParserOutput) -> Option<JsValue> {
82 let mut type_token_pos = vec![];
83 for i in 0..c.tokens.len() {
84 let t = &c.tokens[i];
85 if t.is_type_ann {
86 let obj = js_sys::Object::new();
87 js_sys::Reflect::set(&obj, &JsValue::from("line"), &JsValue::from(t.line)).ok()?;
88 js_sys::Reflect::set(&obj, &JsValue::from("start"), &JsValue::from(t.start)).ok()?;
89 if i < c.tokens.len() - 1 && c.tokens[i + 1].is_type_ann {
90 js_sys::Reflect::set(
91 &obj,
92 &JsValue::from("end"),
93 &JsValue::from(c.tokens[i + 1].start),
94 )
95 .ok()?;
96 } else {
97 js_sys::Reflect::set(&obj, &JsValue::from("end"), &JsValue::from(t.end)).ok()?;
98 }
99 type_token_pos.push(obj);
100 }
101 }
102 Some(type_token_pos.into())
103}
104
105#[wasm_bindgen]
106extern "C" {
107 #[wasm_bindgen(js_namespace = console)]
108 pub fn log(s: &str);
109}
110
111#[wasm_bindgen(start)]
112fn start() {
113 console_error_panic_hook::set_once()
114}
115
116#[wasm_bindgen]
117pub fn lex(code: &str) -> Vec<JsValue> {
118 let tokens = tokenize(code);
119 tokens.iter().filter_map(serialize_token).collect()
120}
121
122#[wasm_bindgen]
123pub fn compile(code: &str, z3_callback: Function) -> JsValue {
124 let mut output = ParserOutput::parse(code);
125 let mut solver = Solver::new_wasm(z3_callback);
126 let type_errors = check(&output, &mut solver);
127 output.errors.extend(type_errors);
128
129 let errors = output
130 .errors
131 .iter()
132 .filter_map(|e| serialize_error(e, &output.tokens))
133 .collect::<Vec<_>>();
134
135 let type_tokens = serialize_type_tokens(&output);
136 let obj = js_sys::Object::new();
137 let _ = js_sys::Reflect::set(&obj, &JsValue::from("errors"), &JsValue::from(errors));
138 let _ = js_sys::Reflect::set(
139 &obj,
140 &JsValue::from("type_tokens"),
141 &JsValue::from(type_tokens),
142 );
143 obj.into()
144}