From be6de4e3b2cf6cda8ac068b9b985cc14865ae21f Mon Sep 17 00:00:00 2001 From: TJ Campanella Date: Sat, 12 Oct 2024 23:09:58 -0400 Subject: [PATCH] Add basic string support --- example-asm/hello.s | 3 +- examples/hello-world | Bin 0 -> 33480 bytes examples/hello-world.rorth | 1 + examples/hello-world.txt | 1 + examples/strings | Bin 0 -> 33560 bytes examples/strings.rorth | 7 ++ examples/strings.txt | 1 + src/main.rs | 231 ++++++++++++++++++++++++++----------- 8 files changed, 176 insertions(+), 68 deletions(-) create mode 100755 examples/hello-world create mode 100644 examples/hello-world.rorth create mode 100644 examples/hello-world.txt create mode 100755 examples/strings create mode 100644 examples/strings.rorth create mode 100644 examples/strings.txt diff --git a/example-asm/hello.s b/example-asm/hello.s index 5c22bb9..a5a2842 100644 --- a/example-asm/hello.s +++ b/example-asm/hello.s @@ -11,8 +11,9 @@ // Setup the parameters to print hello world // and then call the Kernel to do it. +// string stdout syscallnum len syscall4 _start: - mov X0, #1 // 1 = StdOut + mov X0, #1 // 1 = StdOut adr X1, helloworld // string to print mov X2, #13 // length of our string mov X16, #4 // Unix write system call diff --git a/examples/hello-world b/examples/hello-world new file mode 100755 index 0000000000000000000000000000000000000000..cd4f80a9ec029799891932673dc901d5ac1876be GIT binary patch literal 33480 zcmeI5Uu;u#6vt2RI{wK16f%hlxGH4v&tMgJSPWvjY&ev_GMmuM#9zvKvDLeFws&kS zo8B4rU<{G%0Z|DAUqr%mF(mpRu@JHtjG8S7i3Z)s9>9zsMw2$KYa^>tO@SvGS{j z%SqWrsg8JOEFMv@_|61J3i~_yc&1*+StNd5z1(6vmVF*zRjNJ^4CEyJ4rI2r6j(-1 zqFI(oMZ#v-o@xL5UA>jrDgNx>=Jv*W+_HJ=Qw{Z-v+uwv=!<=k&zwz4KiQrnzMlUc z9vN6fem3hI-_O2}eOY!FYZv?T*9ND@1IiAh+@w~$oRC}MBuAiEl3#NS%A;MU?&v_xjeMFd4O^%9R%TU6;? zdTWQNtEe81=t)|bGQ&xeN@=pUA|l^6m+zdH@q^^<0(qW%$DVy{+VOqj%cTYWOrF0x zFTPgc_!}I5spHGz%lT3U5vjM7wVdn8eF)-vN!_F^m1(Oz8H=0Lnuzbxlcq8fiFS&2 zv{78|G-7d`QYPo0la7dHzAH&{#REK)vLY26eGfvhOm0K8AciNXQZ13Il?4xPb{?Tva(L>#*zBSIRo&BJ;cj{Q>g=-VthwmJE z`S^#S#K@JMY4?TE(HFFBq2jro6FuGi-<*7{@zv^gZ*@@K6$^cO8=?xv6kkuAOG-u)6efczhU3v!6zU71D0)YPXGV_ literal 0 HcmV?d00001 diff --git a/examples/hello-world.rorth b/examples/hello-world.rorth new file mode 100644 index 0000000..8120bca --- /dev/null +++ b/examples/hello-world.rorth @@ -0,0 +1 @@ +"Hello, World" 1 12 write diff --git a/examples/hello-world.txt b/examples/hello-world.txt new file mode 100644 index 0000000..1856e9b --- /dev/null +++ b/examples/hello-world.txt @@ -0,0 +1 @@ +Hello, World \ No newline at end of file diff --git a/examples/strings b/examples/strings new file mode 100755 index 0000000000000000000000000000000000000000..c19491a9497d40639622e7c85cf160b4518481f0 GIT binary patch literal 33560 zcmeI5T}&KR6vxjDOD(v3v>}zol8&|#t%QZvC>o=?4Okk{V%@}z#tf95mbI{p>`CRh`U@?fBi*gh0!AN)vaHY884ASY%2RQb}NmjlQixTtz@rc*Y)bari$k)wr-KpRI?YS z4*fw%o*&tDJy;P6riShH{?svYbvh4=OKoDhZW?W-(|2@gKJR1BVNSS3iB7DwS9!>q zTP^uoqOFl=SdT=v$2c&*z7HRF@`c<*qEqrEC*!`Hvxi;R%LBncQqb>0=GL}(Eh8uK z1WR2H*P6BVO#A20@;#84;!kuIZs-2Ts?AS7Sy8?@@gG?8>S8UHFU7D)J&Bq`^L|#Y zbS$#}q7AP0vo7IbVUy3^#yV5_9Yi(U-p*dlF1FNlnJaVtiDmO5jh&xe^0ma9eGQRq zzVPmbFl(_p*`nKD^4!C}9NLk4;k}_7)9ZOmV|UIZS)9IoTyKGA9c%l1c7*6& zKA+_2=I1EcWZyD&iOD(CaDX3EWv&*k=h@wH(`YQ-Sj=@rw(%skLUzgDWH}t}bG7gi zD`1zn#oT(FB4ro`0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JQJ|0K}I$L^ZA zEikoFu}11tYh>-x^KW&wtH%o|eQ0nMEgu}9t|j$r%IZ8cJdx%(#n+)HCuU}Y9-_Y2 zWG# zSv_UChkm0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd& z00JNY0w4eaAOHgY0RfG3$hXYJ#gace&hK^jYg2SuE;Hms1ZmX0hkJTsy>7G_buFf$ z*BiBAqnR?|W^J=cS@cGGURb_wF5fxN=8fdt1+w27N2{8vOGL{{3;a%;zdJ8l&2#mm zu3q8la#t6+y2{n}yE^DSEDd zMzg6m#9~bpZE2*a(b^D+8WcAGy}L zmmEFva?$Fpz3=qyJN-#0Hu%%_0nh00@J@A0D1Em5Z2K#nm(LxnJW%w(t>Tvse|=!j zXqB(PceCt~eC2X~+p*&GnWN)}?il;*c-BMTWTt(z|C!NK(R*^PucXqmW0hyDeMO(w zzjP+5T)fcr>&;u`|F`j}wNHIDn$dW^|I5*nCx3qR>VxO9F1`6~#f{dzn?l*W0eKYZ EcY(axL;wH) literal 0 HcmV?d00001 diff --git a/examples/strings.rorth b/examples/strings.rorth new file mode 100644 index 0000000..0fa62ff --- /dev/null +++ b/examples/strings.rorth @@ -0,0 +1,7 @@ +" Loves to eat. " " Fox" "Red" "The " rot rot rot +1 4 write +1 3 write +1 4 write +1 15 write + + diff --git a/examples/strings.txt b/examples/strings.txt new file mode 100644 index 0000000..1af21f9 --- /dev/null +++ b/examples/strings.txt @@ -0,0 +1 @@ +The Red Fox Loves to eat. \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c95a6bd..eb65815 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ enum OpKind { Mult, Div, Print, + Write, Equals, Dup, Swap, @@ -33,10 +34,16 @@ enum OpKind { LT, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, PartialEq, PartialOrd)] +enum OpValue { + IntVal(u64), + StringVal(String), +} + +#[derive(Debug, Clone)] struct Op { kind: OpKind, - value: Option, + value: Option, } fn parse_file(filename: String) -> Result, ()> { @@ -59,96 +66,128 @@ fn parse_word_as_op(lines: Vec) -> Vec { if let Some(comment_ind) = comment { line = line.chars().take(comment_ind).collect(); } - let words: Vec<&str> = line.split_ascii_whitespace().collect(); + let words: Vec<&str> = line.split_inclusive(['\n', '"', ' ']).collect(); + let mut curr_string: String = String::new(); for word in words { // Exhaustive handling of OpKinds in parse_word_as_op - const_assert!(OpKind::COUNT == 18); - if let Ok(num) = word.parse::() { + const_assert!(OpKind::COUNT == 19); + use OpValue::{IntVal, StringVal}; + let trimmed_word = word.trim(); + // println!("word |{word}|"); + // println!("tw |{trimmed_word}|"); + if trimmed_word.is_empty() && curr_string.is_empty() { + continue; + } + if let Ok(num) = trimmed_word.parse::() { + result.push(Op { + kind: OpKind::Push, + value: Some(IntVal(num)), + }); + } else if curr_string.starts_with('"') + && curr_string.ends_with('"') + && curr_string.chars().count() > 1 + { result.push(Op { kind: OpKind::Push, - value: Some(num), + value: Some(StringVal(curr_string.clone().replace('"', ""))), }); - } else if word == "+" { + curr_string = String::new(); + } else if word == "\"" && curr_string.is_empty() { + curr_string += word; + } else if word == "\"" && !curr_string.is_empty() { + result.push(Op { + kind: OpKind::Push, + value: Some(StringVal(curr_string.clone().replace('"', ""))), + }); + curr_string = String::new(); + } else if !curr_string.is_empty() { + curr_string += word; + } else if trimmed_word == "+" { result.push(Op { kind: OpKind::Plus, value: None, }); - } else if word == "-" { + } else if trimmed_word == "-" { result.push(Op { kind: OpKind::Minus, value: None, }); - } else if word == "*" { + } else if trimmed_word == "*" { result.push(Op { kind: OpKind::Mult, value: None, }); - } else if word == "/" { + } else if trimmed_word == "/" { result.push(Op { kind: OpKind::Div, value: None, }); - } else if word == "print" { + } else if trimmed_word == "print" { result.push(Op { kind: OpKind::Print, value: None, }); - } else if word == "=" { + } else if trimmed_word == "write" { + result.push(Op { + kind: OpKind::Write, + value: None, + }); + } else if trimmed_word == "=" { result.push(Op { kind: OpKind::Equals, value: None, }); - } else if word == "dup" { + } else if trimmed_word == "dup" { result.push(Op { kind: OpKind::Dup, value: None, }); - } else if word == "swap" { + } else if trimmed_word == "swap" { result.push(Op { kind: OpKind::Swap, value: None, }); - } else if word == "rot" { + } else if trimmed_word == "rot" { result.push(Op { kind: OpKind::Rot, value: None, }); - } else if word == "drop" { + } else if trimmed_word == "drop" { result.push(Op { kind: OpKind::Drop, value: None, }); - } else if word == "over" { + } else if trimmed_word == "over" { result.push(Op { kind: OpKind::Over, value: None, }); - } else if word == "if" { + } else if trimmed_word == "if" { result.push(Op { kind: OpKind::If, value: None, }); - } else if word == "while" { + } else if trimmed_word == "while" { result.push(Op { kind: OpKind::While, value: None, }); - } else if word == "do" { + } else if trimmed_word == "do" { result.push(Op { kind: OpKind::Do, value: None, }); - } else if word == "end" { + } else if trimmed_word == "end" { result.push(Op { kind: OpKind::End, value: None, }); - } else if word == ">" { + } else if trimmed_word == ">" { result.push(Op { kind: OpKind::GT, value: None, }); - } else if word == "<" { + } else if trimmed_word == "<" { result.push(Op { kind: OpKind::LT, value: None, @@ -156,6 +195,7 @@ fn parse_word_as_op(lines: Vec) -> Vec { } else { panic!("Unknown word: {word}") } + // println!("curr |{curr_string}|"); } } @@ -171,20 +211,21 @@ fn cross_reference_blocks(program: &mut Vec, ip_start: usize) { let mut curr_do = None; let mut curr_do_ip = 0; while ip < program.len() { - let mut op = program[ip]; + let mut op = program[ip].clone(); // Exhaustive handling of Ops in cross_reference_blocks. // Remember not all need to be accounted for here only Ops that form blocks. - const_assert!(OpKind::COUNT == 18); + const_assert!(OpKind::COUNT == 19); + use OpValue::IntVal; if op.kind == OpKind::If { if curr_if.is_none() && op.value.is_none() { - curr_if = Some(op); + curr_if = Some(op.clone()); curr_if_ip = ip; } else if curr_if.is_some() && op.value.is_none() { cross_reference_blocks(program, ip); } } else if op.kind == OpKind::While { if curr_while.is_none() && op.value.is_none() { - curr_while = Some(op); + curr_while = Some(op.clone()); curr_while_ip = ip; } else if curr_while.is_some() && op.value.is_none() { cross_reference_blocks(program, ip); @@ -198,30 +239,42 @@ fn cross_reference_blocks(program: &mut Vec, ip_start: usize) { cross_reference_blocks(program, ip); } } else if op.kind == OpKind::End { - if let Some(mut if_op) = curr_if { + if let Some(ref mut if_op) = curr_if { if if_op.value.is_none() && op.value.is_none() { - if_op.value = (ip + 1).try_into().ok(); - op.value = ip.try_into().ok(); - program[curr_if_ip] = if_op; - program[ip] = op; + let ip64: Option = (ip + 1).try_into().ok(); + if let Some(ip64) = ip64 { + if_op.value = Some(IntVal(ip64)); + } + let ip64: Option = ip.try_into().ok(); + if let Some(ip64) = ip64 { + op.value = Some(IntVal(ip64)); + } + program[curr_if_ip] = if_op.clone(); + program[ip] = op.clone(); curr_if = None; curr_if_ip = 0; } } - if let Some(mut while_op) = curr_while { - if let Some(mut do_op) = curr_do { + if let Some(ref mut while_op) = curr_while { + if let Some(ref mut do_op) = curr_do { if while_op.value.is_none() && op.value.is_none() { - while_op.value = Some(0); - op.value = curr_while_ip.try_into().ok(); + while_op.value = Some(IntVal(0)); + let curr_while_ip64: Option = curr_while_ip.try_into().ok(); + if let Some(curr_while_ip64) = curr_while_ip64 { + op.value = Some(IntVal(curr_while_ip64)); + } program[ip] = op; - program[curr_while_ip] = while_op; + program[curr_while_ip] = while_op.clone(); curr_while = None; curr_while_ip = 0; if do_op.value.is_none() { - do_op.value = (ip + 1).try_into().ok(); - program[curr_do_ip] = do_op; + let ip64: Option = (ip + 1).try_into().ok(); + if let Some(ip64) = ip64 { + do_op.value = Some(IntVal(ip64)); + } + program[curr_do_ip] = do_op.clone(); curr_do = None; curr_do_ip = 0; } @@ -238,41 +291,42 @@ fn simulate_program(program: &[Op]) { let mut ip = 0; while ip < program.len() { let op = &program[ip]; + use OpValue::{IntVal, StringVal}; match op.kind { OpKind::Push => { - if let Some(val) = op.value { - stack.push(val); + if let Some(val) = &op.value { + stack.push(val.clone()); } ip += 1; } OpKind::Plus => { - if let Some(a) = stack.pop() { - if let Some(b) = stack.pop() { - stack.push(a + b); + if let Some(IntVal(a)) = stack.pop() { + if let Some(IntVal(b)) = stack.pop() { + stack.push(IntVal(a + b)); } } ip += 1; } OpKind::Minus => { - if let Some(a) = stack.pop() { - if let Some(b) = stack.pop() { - stack.push(b - a); + if let Some(IntVal(a)) = stack.pop() { + if let Some(IntVal(b)) = stack.pop() { + stack.push(IntVal(b - a)); } } ip += 1; } OpKind::Mult => { - if let Some(a) = stack.pop() { - if let Some(b) = stack.pop() { - stack.push(a * b); + if let Some(IntVal(a)) = stack.pop() { + if let Some(IntVal(b)) = stack.pop() { + stack.push(IntVal(a * b)); } } ip += 1; } OpKind::Div => { - if let Some(a) = stack.pop() { - if let Some(b) = stack.pop() { - stack.push(b / a); + if let Some(IntVal(a)) = stack.pop() { + if let Some(IntVal(b)) = stack.pop() { + stack.push(IntVal(b / a)); } } ip += 1; @@ -280,13 +334,14 @@ fn simulate_program(program: &[Op]) { OpKind::Equals => { if let Some(a) = stack.pop() { if let Some(b) = stack.pop() { - stack.push((b == a).into()); + stack.push(IntVal((b == a).into())); } } ip += 1; } OpKind::Print => { - if let Some(a) = stack.pop() { + let op = stack.pop(); + if let Some(IntVal(a)) = op { let mut a = format!("{a}"); if a.len() < 20 { let num_to_pad = 20 - a.len(); @@ -298,9 +353,20 @@ fn simulate_program(program: &[Op]) { } ip += 1; } + OpKind::Write => { + stack.pop(); + if let Some(IntVal(fd)) = stack.pop() { + if let Some(StringVal(string)) = stack.pop() { + if fd == 1 { + print!("{string}"); + } + } + } + ip += 1; + } OpKind::Dup => { if let Some(a) = stack.pop() { - stack.push(a); + stack.push(a.clone()); stack.push(a); } ip += 1; @@ -333,7 +399,7 @@ fn simulate_program(program: &[Op]) { OpKind::Over => { if let Some(a) = stack.pop() { if let Some(b) = stack.pop() { - stack.push(b); + stack.push(b.clone()); stack.push(a); stack.push(b); } @@ -341,10 +407,10 @@ fn simulate_program(program: &[Op]) { ip += 1; } OpKind::If | OpKind::Do => { - if let Some(a) = stack.pop() { + if let Some(IntVal(a)) = stack.pop() { if a == 1 { ip += 1; - } else if let Some(ind) = op.value { + } else if let Some(IntVal(ind)) = op.value { if let Ok(ind) = ind.try_into() { ip = ind; } @@ -355,7 +421,7 @@ fn simulate_program(program: &[Op]) { ip += 1; } OpKind::End => { - if let Some(ind) = op.value { + if let Some(IntVal(ind)) = op.value { if let Ok(ind) = ind.try_into() { if ind != ip { ip = ind; @@ -368,7 +434,7 @@ fn simulate_program(program: &[Op]) { OpKind::GT => { if let Some(a) = stack.pop() { if let Some(b) = stack.pop() { - stack.push((b > a).into()); + stack.push(IntVal((b > a).into())); } } ip += 1; @@ -376,7 +442,7 @@ fn simulate_program(program: &[Op]) { OpKind::LT => { if let Some(a) = stack.pop() { if let Some(b) = stack.pop() { - stack.push((b < a).into()); + stack.push(IntVal((b < a).into())); } } ip += 1; @@ -432,14 +498,31 @@ fn compile_program_darwin_arm64(program: &[Op], filename: &str) { let _ = file.write(b"_start: \n"); let mut ip = 0; + let strings: Vec<&Op> = program + .iter() + .filter(|op| op.kind == OpKind::Push) + .collect(); while ip < program.len() { let op = &program[ip]; + use OpValue::{IntVal, StringVal}; match op.kind { OpKind::Push => { - if let Some(val) = op.value { + if let Some(IntVal(val)) = &op.value { let _ = file.write(b" // push \n"); let _ = file.write(format!(" ldr x0, ={val}\n").as_bytes()); - let _ = file.write(" str x0, [sp, #-16]!\n".to_string().as_bytes()); + let _ = file.write(b" str x0, [sp, #-16]!\n"); + } else if let Some(StringVal(val)) = &op.value { + let val_idx = strings + .iter() + .position(|op| op.value == Some(OpValue::StringVal(val.clone()))); + if let Some(val) = val_idx { + let _ = file.write(b" // push \n"); + let _ = + file.write(format!(" adrp x0, string{val}@PAGE\n").as_bytes()); + let _ = file + .write(format!(" add x0, x0, string{val}@PAGEOFF\n").as_bytes()); + let _ = file.write(b" str x0, [sp, #-16]!\n"); + } } ip += 1; } @@ -519,6 +602,15 @@ fn compile_program_darwin_arm64(program: &[Op], filename: &str) { let _ = file.write(b" bl print\n"); ip += 1; } + OpKind::Write => { + let _ = file.write(b" // write \n"); + let _ = file.write(b" ldr X2, [sp], #16\n"); + let _ = file.write(b" ldr X0, [sp], #16\n"); + let _ = file.write(b" ldr X1, [sp], #16\n"); + let _ = file.write(b" mov X16, #4\n"); + let _ = file.write(b" svc #0x80\n"); + ip += 1; + } OpKind::Over => { let _ = file.write(b" // over \n"); let _ = file.write(b" ldr x0, [sp], #16\n"); @@ -532,7 +624,7 @@ fn compile_program_darwin_arm64(program: &[Op], filename: &str) { let _ = file.write(b" // if \n"); let _ = file.write(b" ldr x0, [sp], #16\n"); let _ = file.write(b" cmp x0, #0\n"); - if let Some(ind) = op.value { + if let Some(IntVal(ind)) = op.value { let _ = file.write(format!(" beq addr_{ind}\n").as_bytes()); } ip += 1; @@ -545,13 +637,13 @@ fn compile_program_darwin_arm64(program: &[Op], filename: &str) { let _ = file.write(b" // do \n"); let _ = file.write(b" ldr x0, [sp], #16\n"); let _ = file.write(b" cmp x0, #0\n"); - if let Some(ind) = op.value { + if let Some(IntVal(ind)) = op.value { let _ = file.write(format!(" beq addr_{ind}\n").as_bytes()); } ip += 1; } OpKind::End => { - if let Some(ind) = op.value { + if let Some(IntVal(ind)) = op.value { if let Ok(ind) = TryInto::::try_into(ind) { if ind != ip { let _ = file.write(format!(" b addr_{ind}\n").as_bytes()); @@ -589,6 +681,11 @@ fn compile_program_darwin_arm64(program: &[Op], filename: &str) { let _ = file.write(b".data\n"); let _ = file.write(b" num: .zero 20\n"); let _ = file.write(b" newline: .asciz \"\\n\" \n"); + for (idx, string) in strings.iter().enumerate() { + if let Some(OpValue::StringVal(val)) = &string.value { + let _ = file.write(format!(" string{idx}: .asciz \"{val}\" \n").as_bytes()); + } + } } }