remove unwraps, fix utf8
This commit is contained in:
parent
e492ad9a4a
commit
ace046624d
18 changed files with 118 additions and 83 deletions
|
@ -1,4 +1,4 @@
|
|||
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs, cell::RefCell, rc::Rc};
|
||||
use std::{path::PathBuf, io::{self, Read, IsTerminal}, cell::RefCell, rc::Rc};
|
||||
use clap::{Parser as ArgParser, ColorChoice};
|
||||
use matrix_lang::prelude::*;
|
||||
use repl::Repl;
|
||||
|
@ -35,8 +35,8 @@ pub struct Args {
|
|||
|
||||
pub enum Mode {
|
||||
Repl,
|
||||
Execute(String),
|
||||
Compile(String, PathBuf),
|
||||
Execute(Vec<u8>),
|
||||
Compile(Vec<u8>, PathBuf),
|
||||
}
|
||||
|
||||
pub struct State<'a> {
|
||||
|
@ -46,18 +46,26 @@ pub struct State<'a> {
|
|||
color: bool,
|
||||
}
|
||||
|
||||
pub fn error(err: Exception, state: &State) {
|
||||
if state.color {
|
||||
println!("\x1b[31mError:\x1b[0m {err}");
|
||||
} else {
|
||||
println!("Error: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
pub fn new (args: Args) -> (Self, Mode) {
|
||||
pub fn new (args: Args) -> Result<(Self, Mode)> {
|
||||
|
||||
let stdin = read_stdin();
|
||||
|
||||
let file;
|
||||
let mut buffer = Vec::new();
|
||||
if let Some(path) = &args.file {
|
||||
file = Some(fs::read_to_string(path).unwrap());
|
||||
} else if stdin.len() > 0 {
|
||||
file = Some(stdin);
|
||||
let mut f = File::open(path)?;
|
||||
f.read_to_end(&mut buffer)?;
|
||||
} else {
|
||||
file = None;
|
||||
let mut stdin = io::stdin();
|
||||
if !stdin.is_terminal() {
|
||||
stdin.read_to_end(&mut buffer)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mode;
|
||||
|
@ -74,11 +82,10 @@ impl<'a> State<'a> {
|
|||
PathBuf::from("matc.out")
|
||||
}
|
||||
};
|
||||
let file = file.unwrap_or(String::new());
|
||||
mode = Mode::Compile(file, path);
|
||||
mode = Mode::Compile(buffer, path);
|
||||
repl = false;
|
||||
} else if let Some(file) = file {
|
||||
mode = Mode::Execute(file);
|
||||
} else if buffer.len() > 0 {
|
||||
mode = Mode::Execute(buffer);
|
||||
repl = false;
|
||||
} else {
|
||||
mode = Mode::Repl;
|
||||
|
@ -106,16 +113,16 @@ impl<'a> State<'a> {
|
|||
Some(ColorChoice::Never) => false,
|
||||
};
|
||||
|
||||
(Self {
|
||||
Ok((Self {
|
||||
parser,
|
||||
vm: Rc::new(RefCell::new(vm)),
|
||||
compiler,
|
||||
color,
|
||||
}, mode)
|
||||
}, mode))
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, fun: Rc<Function>) -> Result<Value> {
|
||||
let val = self.vm.try_borrow_mut().unwrap().run(fun)?;
|
||||
let val = self.vm.borrow_mut().run(fun)?;
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
|
@ -125,47 +132,37 @@ impl<'a> State<'a> {
|
|||
Ok(fun)
|
||||
}
|
||||
|
||||
pub fn load_program(&mut self, body: String) -> Result<Rc<Function>> {
|
||||
match Program::load(&body)? {
|
||||
pub fn load_program(&mut self, mut buffer: Vec<u8>) -> Result<Rc<Function>> {
|
||||
let res = Program::load(buffer.as_mut_slice())?;
|
||||
match res {
|
||||
Some(fun) => {
|
||||
Ok(fun)
|
||||
},
|
||||
None => {
|
||||
let body = buffer_to_string(buffer)?;
|
||||
self.compile(body)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error(err: Exception, state: &State) {
|
||||
if state.color {
|
||||
println!("\x1b[31mError:\x1b[0m {err}");
|
||||
} else {
|
||||
println!("Error: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
fn read_stdin() -> String {
|
||||
let mut buffer = String::new();
|
||||
let mut stdin = io::stdin();
|
||||
if stdin.is_terminal() {
|
||||
return String::new();
|
||||
}
|
||||
stdin.read_to_string(&mut buffer).unwrap();
|
||||
buffer
|
||||
fn buffer_to_string(buffer: Vec<u8>) -> Result<String> {
|
||||
String::from_utf8(buffer)
|
||||
.map_err(|e| exception!(IO_EXCEPTION, "{e}"))
|
||||
}
|
||||
|
||||
fn handle_mode(state: &mut State, mode: Mode) -> Result<()> {
|
||||
match mode {
|
||||
Mode::Repl => {
|
||||
let mut repl = Repl::new(state);
|
||||
repl.run();
|
||||
repl.run()?;
|
||||
},
|
||||
Mode::Execute(body) => {
|
||||
let fun = state.load_program(body)?;
|
||||
Mode::Execute(buffer) => {
|
||||
let fun = state.load_program(buffer)?;
|
||||
state.execute(fun)?;
|
||||
},
|
||||
Mode::Compile(body, path) => {
|
||||
Mode::Compile(buffer, path) => {
|
||||
let body = buffer_to_string(buffer)?;
|
||||
let fun = state.compile(body)?;
|
||||
let mut file = File::create(path).map_err(|e|
|
||||
exception!(IO_EXCEPTION, "{e}")
|
||||
|
@ -176,12 +173,17 @@ fn handle_mode(state: &mut State, mode: Mode) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
fn load() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
let (mut state, mode) = State::new(args);
|
||||
|
||||
let (mut state, mode) = State::new(args)?;
|
||||
if let Err(e) = handle_mode(&mut state, mode) {
|
||||
error(e, &state);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = load() {
|
||||
println!("\x1b[31mFatal:\x1b[0m {e}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@ impl<'s, 'a> Repl<'s, 'a> {
|
|||
Ok(val)
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
pub fn run(&mut self) -> Result<()> {
|
||||
|
||||
let interupt = self.state.vm.borrow().interupt();
|
||||
ctrlc::set_handler(move || {
|
||||
interupt.store(Interupt::KeyboardInterupt as usize, Ordering::SeqCst);
|
||||
}).unwrap();
|
||||
}).exception()?;
|
||||
|
||||
let config = Config::builder()
|
||||
.indent_size(4)
|
||||
|
@ -39,10 +39,10 @@ impl<'s, 'a> Repl<'s, 'a> {
|
|||
|
||||
let histfile = std::env::var("MATRIX_HISTORY").ok();
|
||||
|
||||
let mut rl = Editor::with_config(config).unwrap();
|
||||
let mut rl = Editor::with_config(config).exception()?;
|
||||
rl.set_helper(Some(helper));
|
||||
if let Some(hf) = &histfile {
|
||||
rl.load_history(hf).ok();
|
||||
rl.load_history(hf).exception()?;
|
||||
}
|
||||
|
||||
loop {
|
||||
|
@ -52,7 +52,7 @@ impl<'s, 'a> Repl<'s, 'a> {
|
|||
Err(_) => continue,
|
||||
};
|
||||
|
||||
rl.add_history_entry(&line).ok();
|
||||
rl.add_history_entry(&line).exception()?;
|
||||
|
||||
match self.execute(line) {
|
||||
Ok(val) => {
|
||||
|
@ -66,12 +66,14 @@ impl<'s, 'a> Repl<'s, 'a> {
|
|||
}
|
||||
Err(err) => crate::error(err, &self.state),
|
||||
};
|
||||
std::io::stdout().flush().ok();
|
||||
std::io::stdout().flush().exception()?;
|
||||
}
|
||||
|
||||
if let Some(hf) = &histfile {
|
||||
rl.save_history(hf).ok();
|
||||
rl.save_history(hf).exception()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@ pub struct Program {
|
|||
const PROGRAM_HEADER: [u8; 5] = [0x00, 0x4d, 0x41, 0x54, 0x0a];
|
||||
|
||||
impl Program {
|
||||
pub fn load(body: &str) -> Result<Option<Rc<Function>>> {
|
||||
let mut bytes = body.as_bytes();
|
||||
pub fn load(bytes: &mut [u8]) -> Result<Option<Rc<Function>>> {
|
||||
if bytes.len() < 6 {
|
||||
return Ok(None)
|
||||
}
|
||||
|
@ -22,7 +21,8 @@ impl Program {
|
|||
if header != &PROGRAM_HEADER {
|
||||
return Ok(None)
|
||||
}
|
||||
let mut s = ProgramDeserializer::from(&mut bytes);
|
||||
let mut a = &bytes[..];
|
||||
let mut s = ProgramDeserializer::from(&mut a);
|
||||
let program = <Self>::deserialize(&mut s)?;
|
||||
s.finish()?;
|
||||
Ok(Some(program.fun.clone()))
|
||||
|
|
|
@ -142,7 +142,7 @@ impl<'c> Compiler<'c> {
|
|||
fn collapse_scopes(&mut self, top_scope: usize) {
|
||||
let mut cutoff = usize::MAX;
|
||||
while self.scopes.len() > top_scope {
|
||||
cutoff = self.scopes.pop().unwrap()
|
||||
cutoff = self.scopes.pop().expect("bypassed compiler scope check")
|
||||
}
|
||||
if cutoff < self.locals.len() {
|
||||
self.emit(Instruction::DiscardLocals((self.locals.len() - cutoff) as u16), self.last_pos());
|
||||
|
|
|
@ -286,7 +286,7 @@ impl Lexer {
|
|||
buf.push(char::from_u32(
|
||||
n1.to_digit(16).ok_or(error!("invalid digit '{n1}'"))? * 16 +
|
||||
n2.to_digit(16).ok_or(error!("invalid digit '{n2}'"))?
|
||||
).unwrap());
|
||||
).expect("bypassed digit check"));
|
||||
},
|
||||
'u' => {
|
||||
self.next_expect('{')?;
|
||||
|
@ -416,7 +416,7 @@ impl Lexer {
|
|||
}
|
||||
}
|
||||
|
||||
let last: char = buf.chars().last().unwrap();
|
||||
let last: char = buf.chars().last().unwrap_or('\0');
|
||||
let is_range = initial != '.' && last == '.' && self.peek() == '.';
|
||||
|
||||
if is_range {
|
||||
|
|
|
@ -169,7 +169,7 @@ impl Parser {
|
|||
};
|
||||
}
|
||||
if parts.len() == 1 {
|
||||
Ok((E::List(parts.pop().unwrap()), pos).into())
|
||||
Ok((E::List(parts.pop().expect("bypassed vec length")), pos).into())
|
||||
} else {
|
||||
let codomain = parts.len();
|
||||
let domain = parts[0].len();
|
||||
|
|
|
@ -2,6 +2,7 @@ pub type Result<T> = std::result::Result<T, Exception>;
|
|||
|
||||
pub use crate::value::Value as Value;
|
||||
pub use crate::value::exception::Exception as Exception;
|
||||
pub use crate::value::exception::Except as Except;
|
||||
pub use crate::value::matrix::Matrix as Matrix;
|
||||
pub use crate::value::gc::Gc as Gc;
|
||||
pub use crate::value::hash::ValueMap as ValueMap;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{fmt::{Debug, Display}, error::Error};
|
||||
use std::{fmt::{Debug, Display}, error::Error, io};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[macro_export]
|
||||
|
@ -76,3 +76,24 @@ impl<T> From<Exception> for Result<T> {
|
|||
Err(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Exception {
|
||||
fn from(value: io::Error) -> Self {
|
||||
exception!(IO_EXCEPTION, "{value}")
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Except {
|
||||
type Output;
|
||||
fn exception(self) -> Result<Self::Output>;
|
||||
}
|
||||
|
||||
impl<T, E: std::error::Error> Except for std::result::Result<T, E> {
|
||||
type Output = T;
|
||||
|
||||
fn exception(self) -> Result<Self::Output> {
|
||||
self.map_err(|e| {
|
||||
Exception::msg("unknown", format!("{e}").as_str())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ impl Matrix {
|
|||
let values = rows
|
||||
.into_iter()
|
||||
.reduce(|mut a,b| {a.extend(b); a})
|
||||
.unwrap();
|
||||
.ok_or(error!("matrix row smashed"))?;
|
||||
|
||||
Ok(Matrix::new(self.domain + other.domain, self.codomain, values))
|
||||
}
|
||||
|
|
|
@ -217,7 +217,7 @@ impl Vm {
|
|||
let val = self.globals
|
||||
.borrow_mut()
|
||||
.get(&idx)
|
||||
.unwrap()
|
||||
.ok_or(exception!(RUNTIME_EXCEPTION, "undefined global at index {idx}"))?
|
||||
.clone();
|
||||
self.stack.push(val);
|
||||
},
|
||||
|
@ -380,7 +380,7 @@ impl Vm {
|
|||
self.trystack.push(scope);
|
||||
},
|
||||
I::TryEnd => {
|
||||
self.trystack.pop().unwrap();
|
||||
self.trystack.pop().ok_or(exception!(RUNTIME_EXCEPTION, "try stack smashed"))?;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ impl Parse for NativeFuncParams {
|
|||
|
||||
#[proc_macro_attribute]
|
||||
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
|
||||
let itemfn: ItemFn = syn::parse(annotated_item).unwrap();
|
||||
let itemfn: ItemFn = syn::parse(annotated_item).expect("invalid rust function");
|
||||
|
||||
let input: NativeFuncParams = syn::parse(input).unwrap();
|
||||
let input: NativeFuncParams = syn::parse(input).expect("invalid native fn annotation");
|
||||
let arity = input.arity;
|
||||
let variadic = input.variadic.is_some();
|
||||
|
||||
|
|
|
@ -4,30 +4,31 @@ use matrix_lang::prelude::*;
|
|||
use matrix_macros::native_func;
|
||||
use crate::{VmArgs, next, error, unpack_args, unpack_varargs};
|
||||
|
||||
fn to_radix(r: i64, mut n: i64) -> String {
|
||||
fn to_radix(r: i64, mut n: i64) -> Result<String> {
|
||||
let mut result = String::new();
|
||||
let mut idx = 0;
|
||||
if n == 0 {
|
||||
result.push('0');
|
||||
return result
|
||||
return Ok(result)
|
||||
} else if n < 0 {
|
||||
n = -n;
|
||||
idx = 1;
|
||||
result.push('-');
|
||||
}
|
||||
while n != 0 {
|
||||
let c = std::char::from_digit((n % r) as u32, r as u32).unwrap();
|
||||
let c = std::char::from_digit((n % r) as u32, r as u32)
|
||||
.ok_or(exception!(RUNTIME_EXCEPTION, "given radix and digit created invalid number"))?;
|
||||
result.insert(idx, c);
|
||||
n /= r;
|
||||
}
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
fn bin(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [value] = unpack_args!(args);
|
||||
match value {
|
||||
Value::Int(n) => Ok(Value::String(to_radix(2, n).into())),
|
||||
Value::Int(n) => Ok(Value::String(to_radix(2, n)?.into())),
|
||||
_ => error!("bin requires a integer agument")
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +37,7 @@ fn bin(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
fn sex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [value] = unpack_args!(args);
|
||||
match value {
|
||||
Value::Int(n) => Ok(Value::String(to_radix(6, n).into())),
|
||||
Value::Int(n) => Ok(Value::String(to_radix(6, n)?.into())),
|
||||
_ => error!("sex requires a integer agument")
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +46,7 @@ fn sex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
fn oct(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [value] = unpack_args!(args);
|
||||
match value {
|
||||
Value::Int(n) => Ok(Value::String(to_radix(8, n).into())),
|
||||
Value::Int(n) => Ok(Value::String(to_radix(8, n)?.into())),
|
||||
_ => error!("oct requires a integer agument")
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +55,7 @@ fn oct(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
fn hex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [value] = unpack_args!(args);
|
||||
match value {
|
||||
Value::Int(n) => Ok(Value::String(to_radix(16, n).into())),
|
||||
Value::Int(n) => Ok(Value::String(to_radix(16, n)?.into())),
|
||||
_ => error!("hex requires a integer agument")
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +64,7 @@ fn hex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
fn radix(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
||||
let [radix, value] = unpack_args!(args);
|
||||
match (radix, value) {
|
||||
(Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n).into())),
|
||||
(Value::Int(r), Value::Int(n)) => Ok(Value::String(to_radix(r, n)?.into())),
|
||||
_ => error!("radix requires two integer aguments")
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ fn ord(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
if str.len() != 1 {
|
||||
return error!("ord requires a 1 length string")
|
||||
}
|
||||
let char = str.chars().next().unwrap();
|
||||
let char = str.chars().next().unwrap_or('\0');
|
||||
Ok(Value::Int(char as i64))
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ fn file_read(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
return error!("file read requires a file")
|
||||
};
|
||||
let mut contents = String::new();
|
||||
if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) {
|
||||
if let Err(err) = file.borrow_mut().read_to_string(&mut contents) {
|
||||
return error!("cannot read file: '{err}'")
|
||||
};
|
||||
Ok(Value::String(contents.into()))
|
||||
|
@ -119,7 +119,7 @@ fn file_lines(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
return error!("file read requires a file")
|
||||
};
|
||||
let mut contents = String::new();
|
||||
if let Err(err) = file.try_borrow_mut().unwrap().read_to_string(&mut contents) {
|
||||
if let Err(err) = file.borrow_mut().read_to_string(&mut contents) {
|
||||
return error!("cannot read file: '{err}'")
|
||||
};
|
||||
let lines: Vec<Rc<str>> = contents.split_inclusive("\n").map(|s| Rc::from(s)).collect();
|
||||
|
@ -139,7 +139,7 @@ fn file_write(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
return error!("file write requires a file")
|
||||
};
|
||||
let content = format!("{content}");
|
||||
if let Err(err) = file.try_borrow_mut().unwrap().write_all(content.as_bytes()) {
|
||||
if let Err(err) = file.borrow_mut().write_all(content.as_bytes()) {
|
||||
return error!("cannot write file: '{err}'")
|
||||
};
|
||||
Ok(Value::Nil)
|
||||
|
|
|
@ -365,7 +365,7 @@ fn unzip((vm, frame): VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
if vals.len() != 2 {
|
||||
return error!("unzip only works over a iterator of pairs");
|
||||
}
|
||||
let [l, r] = vals.try_into().unwrap();
|
||||
let [l, r] = vals.try_into().map_err(|_| exception!(RUNTIME_EXCEPTION, "can only unzip over a set of pairs"))?;
|
||||
ll.push(l);
|
||||
lr.push(r);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ fn trans(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
let values = mat
|
||||
.cols()
|
||||
.reduce(|mut a, b| {a.extend(b); a})
|
||||
.unwrap()
|
||||
.ok_or(exception!(RUNTIME_EXCEPTION, "matrix values smashed"))?
|
||||
.into_iter()
|
||||
.map(|e| e.clone())
|
||||
.collect();
|
||||
|
@ -164,7 +164,7 @@ fn mat_det(mat: Matrix) -> Result<Value> {
|
|||
.collect::<Vec<Value>>()
|
||||
)
|
||||
.reduce(|mut a, b| {a.extend(b); a})
|
||||
.unwrap();
|
||||
.ok_or(exception!(RUNTIME_EXCEPTION, "matrix values smashed"))?;
|
||||
let sub = Matrix::new(mat.domain - 1, mat.domain - 1, sub_values);
|
||||
let val = mat.get(0, col)?;
|
||||
let part = (val * mat_det(sub)?)?;
|
||||
|
|
|
@ -193,7 +193,7 @@ fn basename(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
};
|
||||
let path = PathBuf::from(value.to_string());
|
||||
match path.file_name() {
|
||||
Some(p) => Ok(Value::String(p.to_str().unwrap().into())),
|
||||
Some(p) => Ok(Value::String(p.to_str().unwrap_or("").into())),
|
||||
None => Ok(Value::String(value.into()))
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ fn dirname(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
Some(p) => p,
|
||||
None => path.as_path()
|
||||
};
|
||||
let str = parent.as_os_str().to_str().unwrap();
|
||||
let str = parent.as_os_str().to_str().unwrap_or("");
|
||||
match str {
|
||||
"" => Ok(Value::String(".".into())),
|
||||
s => Ok(Value::String(s.into()))
|
||||
|
@ -226,7 +226,7 @@ fn realpath(_: VmArgs, args: Vec<Value>) -> Result<Value> {
|
|||
Ok(p) => p,
|
||||
Err(e) => return error!("could not get realpath: {e}")
|
||||
};
|
||||
Ok(Value::String(path.to_str().unwrap().into()))
|
||||
Ok(Value::String(path.to_str().unwrap_or("").into()))
|
||||
}
|
||||
|
||||
|
||||
|
|
8
test.mat
Normal file
8
test.mat
Normal file
|
@ -0,0 +1,8 @@
|
|||
let a = 3
|
||||
{
|
||||
a = 4;
|
||||
const bees = \a => a*2;
|
||||
let j = bees(3);
|
||||
a = j;
|
||||
}
|
||||
let g = a + 'aaaa';
|
BIN
test.matc
Normal file
BIN
test.matc
Normal file
Binary file not shown.
Loading…
Reference in a new issue