stl + brainfuck sample program

This commit is contained in:
2022-02-24 13:29:37 +01:00
parent 2c49433887
commit cee371a2a2
7 changed files with 738 additions and 102 deletions

View File

@@ -32,29 +32,35 @@
<to><integer value="3" /></to> <to><integer value="3" /></to>
<step><integer value="1" /></step> <step><integer value="1" /></step>
<do> <do>
<insert-array> <call function="array-push">
<array><value variable="a" /></array> <arguments>
<value><value variable="i" /></value> <value variable="a" />
</insert-array> <value variable="i" />
</arguments>
</call>
</do> </do>
</for> </for>
<print> <call function="print">
<call function="sum"> <arguments>
<arguments> <call function="sum">
<value variable="a" /> <arguments>
</arguments> <value variable="a" />
</call> </arguments>
</print> </call>
<print> </arguments>
<call function="sum"> </call>
<arguments> <call function="print">
<array> <arguments>
<integer value="11"/> <call function="sum">
<integer value="13"/> <arguments>
<integer value="12"/> <array>
</array> <integer value="11"/>
</arguments> <integer value="13"/>
</call> <integer value="12"/>
</print> </array>
</arguments>
</call>
</arguments>
</call>
</main> </main>
</program> </program>

369
sample/bf.pl.xml Normal file
View File

@@ -0,0 +1,369 @@
<program name="brainfuck">
<main>
<assign variable="prog">
<call function="string-split">
<arguments>
<call function="input">
<arguments />
</call>
<string value="" />
</arguments>
</call>
</assign>
<assign variable="i">
<integer value="0" />
</assign>
<assign variable="p">
<integer value="0" />
</assign>
<assign variable="t">
<array />
</assign>
<for variable="_">
<from><integer value="0" /></from>
<to><integer value="100" /></to>
<step><integer value="1" /></step>
<do>
<call function="array-push">
<arguments>
<value variable="t" />
<integer value="0" />
</arguments>
</call>
</do>
</for>
<while>
<not>
<equal>
<value variable="i" />
<call function="array-length">
<arguments>
<value variable="prog" />
</arguments>
</call>
</equal>
</not>
<do>
<assign variable="c">
<call function="array-get">
<arguments>
<value variable="prog" />
<value variable="i" />
</arguments>
</call>
</assign>
<if>
<equal>
<string value="+" />
<value variable="c" />
</equal>
<then>
<call function="array-set">
<arguments>
<value variable="t" />
<value variable="p" />
<add>
<call function="array-get">
<arguments>
<value variable="t" />
<value variable="p" />
</arguments>
</call>
<integer value="1" />
</add>
</arguments>
</call>
</then>
<else>
<if>
<equal>
<string value="-" />
<value variable="c" />
</equal>
<then>
<call function="array-set">
<arguments>
<value variable="t" />
<value variable="p" />
<subtract>
<call function="array-get">
<arguments>
<value variable="t" />
<value variable="p" />
</arguments>
</call>
<integer value="1" />
</subtract>
</arguments>
</call>
</then>
<else>
<if>
<equal>
<string value=">" />
<value variable="c" />
</equal>
<then>
<assign variable="p">
<add>
<value variable="p" />
<integer value="1" />
</add>
</assign>
</then>
<else>
<if>
<equal>
<string value="&lt;" />
<value variable="c" />
</equal>
<then>
<assign variable="p">
<subtract>
<value variable="p" />
<integer value="1" />
</subtract>
</assign>
</then>
<else>
<if>
<equal>
<string value="." />
<value variable="c" />
</equal>
<then>
<call function="print">
<arguments>
<call function="array-get">
<arguments>
<value variable="t" />
<value variable="p" />
</arguments>
</call>
</arguments>
</call>
</then>
<else>
<if>
<equal>
<string value="," />
<value variable="c" />
</equal>
<then>
</then>
<else>
<if>
<equal>
<string value="[" />
<value variable="c" />
</equal>
<then>
<if>
<not>
<call function="array-get">
<arguments>
<value variable="t" />
<value variable="p" />
</arguments>
</call>
</not>
<then>
<assign variable="open">
<integer value="0" />
</assign>
<assign variable="i">
<add>
<value variable="i" />
<integer value="1" />
</add>
</assign>
<while>
<not>
<and>
<equal>
<value variable="open" />
<integer value="0" />
</equal>
<equal>
<call function="array-get">
<arguments>
<value variable="prog" />
<value variable="i" />
</arguments>
</call>
<string value="]" />
</equal>
</and>
</not>
<do>
<if>
<equal>
<call function="array-get">
<arguments>
<value variable="prog" />
<value variable="i" />
</arguments>
</call>
<string value="[" />
</equal>
<then>
<assign variable="open">
<add>
<value variable="open" />
<integer value="1" />
</add>
</assign>
</then>
</if>
<if>
<equal>
<call function="array-get">
<arguments>
<value variable="prog" />
<value variable="i" />
</arguments>
</call>
<string value="]" />
</equal>
<then>
<assign variable="open">
<subtract>
<value variable="open" />
<integer value="1" />
</subtract>
</assign>
</then>
</if>
<assign variable="i">
<add>
<value variable="i" />
<integer value="1" />
</add>
</assign>
</do>
</while>
</then>
</if>
</then>
<else>
<if>
<equal>
<string value="]" />
<value variable="c" />
</equal>
<then>
<if>
<call function="array-get">
<arguments>
<value variable="t" />
<value variable="p" />
</arguments>
</call>
<then>
<assign variable="open">
<integer value="0" />
</assign>
<assign variable="i">
<subtract>
<value variable="i" />
<integer value="1" />
</subtract>
</assign>
<while>
<not>
<and>
<equal>
<value variable="open" />
<integer value="0" />
</equal>
<equal>
<call function="array-get">
<arguments>
<value variable="prog" />
<value variable="i" />
</arguments>
</call>
<string value="[" />
</equal>
</and>
</not>
<do>
<if>
<equal>
<call function="array-get">
<arguments>
<value variable="prog" />
<value variable="i" />
</arguments>
</call>
<string value="[" />
</equal>
<then>
<assign variable="open">
<subtract>
<value variable="open" />
<integer value="1" />
</subtract>
</assign>
</then>
</if>
<if>
<equal>
<call function="array-get">
<arguments>
<value variable="prog" />
<value variable="i" />
</arguments>
</call>
<string value="]" />
</equal>
<then>
<assign variable="open">
<add>
<value variable="open" />
<integer value="1" />
</add>
</assign>
</then>
</if>
<assign variable="i">
<subtract>
<value variable="i" />
<integer value="1" />
</subtract>
</assign>
</do>
</while>
</then>
</if>
</then>
<else>
</else>
</if>
</else>
</if>
</else>
</if>
</else>
</if>
</else>
</if>
</else>
</if>
</else>
</if>
</else>
</if>
<assign variable="i">
<add>
<value variable="i" />
<integer value="1" />
</add>
</assign>
</do>
</while>
</main>
</program>

View File

@@ -3,14 +3,18 @@
<if> <if>
<integer value="1" /> <integer value="1" />
<then> <then>
<print> <call function="print">
<string value="TRUE" /> <arguments>
</print> <string value="TRUE" />
</arguments>
</call>
</then> </then>
<else> <else>
<print> <call function="print">
<arguments>
<string value="FALSE" /> <string value="FALSE" />
</print> </arguments>
</call>
</else> </else>
</if> </if>
</main> </main>

40
sample/stl.pl.xml Normal file
View File

@@ -0,0 +1,40 @@
<program>
<main>
<call function="print">
<arguments>
<integer value="1" />
</arguments>
</call>
<assign variable="arr">
<array>
<float value="0.1" />
</array>
</assign>
<call function="array-set">
<arguments>
<value variable="arr" />
<integer value="0" />
<float value="0.5" />
</arguments>
</call>
<each variable="v">
<value variable="arr" />
<do>
<call function="print">
<arguments>
<value variable="v" />
</arguments>
</call>
</do>
</each>
<call function="print">
<arguments>
<call function="array-pop">
<arguments>
<value variable="arr" />
</arguments>
</call>
</arguments>
</call>
</main>
</program>

View File

@@ -24,8 +24,10 @@
</arguments> </arguments>
</call> </call>
</assign> </assign>
<print> <call function="print">
<value variable="b" /> <arguments>
</print> <value variable="b" />
</arguments>
</call>
</main> </main>
</program> </program>

View File

@@ -6,9 +6,11 @@
<while> <while>
<value variable="a" /> <value variable="a" />
<do> <do>
<print> <call function="print">
<value variable="a" /> <arguments>
</print> <value variable="a" />
</arguments>
</call>
<assign variable="a"> <assign variable="a">
<subtract> <subtract>
<value variable="a" /> <value variable="a" />

View File

@@ -37,6 +37,9 @@ pub enum Instruction {
And(Vec<Instruction>), And(Vec<Instruction>),
Or(Vec<Instruction>), Or(Vec<Instruction>),
Not(Box<Instruction>), Not(Box<Instruction>),
Equal(Box<Instruction>, Box<Instruction>),
Greater(Box<Instruction>, Box<Instruction>),
Lower(Box<Instruction>, Box<Instruction>),
Call(Box<Instruction>, Vec<Instruction>), Call(Box<Instruction>, Vec<Instruction>),
CallNamed(String, Vec<Instruction>), CallNamed(String, Vec<Instruction>),
Return(Box<Instruction>), Return(Box<Instruction>),
@@ -51,14 +54,10 @@ pub enum Instruction {
}, },
Each(String, Box<Instruction>, Vec<Instruction>), Each(String, Box<Instruction>, Vec<Instruction>),
While(Box<Instruction>, Vec<Instruction>), While(Box<Instruction>, Vec<Instruction>),
Print(Box<Instruction>),
AssignArray(Box<Instruction>, Box<Instruction>, Box<Instruction>),
InsertArray(Box<Instruction>, Box<Instruction>),
} }
impl Instruction { impl Instruction {
pub fn new(node: Node) -> Result<Instruction, Box<dyn Error>> { pub fn new(node: Node) -> Result<Instruction, Box<dyn Error>> {
// println!("parsing '{}'", util::tag_name(&node));
Ok(match util::tag_name(&node).as_str() { Ok(match util::tag_name(&node).as_str() {
"value" => Instruction::Value( "value" => Instruction::Value(
node.attribute("variable") node.attribute("variable")
@@ -100,6 +99,39 @@ impl Instruction {
node.first_element_child() node.first_element_child()
.ok_or("missing value child element in 'not' tag")?, .ok_or("missing value child element in 'not' tag")?,
)?)), )?)),
"equal" => {
let children: Vec<Node> = node.children().filter(Node::is_element).collect();
if children.len() == 2 {
Instruction::Equal(
Box::new(Instruction::new(children[0])?),
Box::new(Instruction::new(children[1])?),
)
} else {
Err("bad child count in 'equal' tag")?
}
}
"greater" => {
let children: Vec<Node> = node.children().filter(Node::is_element).collect();
if children.len() == 2 {
Instruction::Greater(
Box::new(Instruction::new(children[0])?),
Box::new(Instruction::new(children[1])?),
)
} else {
Err("bad child count in 'greater' tag")?
}
}
"lower" => {
let children: Vec<Node> = node.children().filter(Node::is_element).collect();
if children.len() == 2 {
Instruction::Lower(
Box::new(Instruction::new(children[0])?),
Box::new(Instruction::new(children[1])?),
)
} else {
Err("bad child count in 'lower' tag")?
}
}
"call" => { "call" => {
if let Some(function) = node.attribute("function") { if let Some(function) = node.attribute("function") {
Instruction::CallNamed( Instruction::CallNamed(
@@ -180,39 +212,6 @@ impl Instruction {
)?), )?),
Instruction::from_children(util::find_node(&node, "do")?)?, Instruction::from_children(util::find_node(&node, "do")?)?,
), ),
"print" => Instruction::Print(Box::new(Instruction::new(
node.first_element_child()
.ok_or("missing value child element in 'print' tag")?,
)?)),
"assign-array" => Instruction::AssignArray(
Box::new(Instruction::new(
util::find_node(&node, "array")?
.first_element_child()
.ok_or("missing 'array' in 'assign-array' tag")?,
)?),
Box::new(Instruction::new(
util::find_node(&node, "index")?
.first_element_child()
.ok_or("missing 'index' in 'assign-array' tag")?,
)?),
Box::new(Instruction::new(
util::find_node(&node, "value")?
.first_element_child()
.ok_or("missing 'value' in 'assign-array' tag")?,
)?),
),
"insert-array" => Instruction::InsertArray(
Box::new(Instruction::new(
util::find_node(&node, "array")?
.first_element_child()
.ok_or("missing 'array' in 'insert-array' tag")?,
)?),
Box::new(Instruction::new(
util::find_node(&node, "value")?
.first_element_child()
.ok_or("missing 'value' in 'insert-array' tag")?,
)?),
),
tag => Err(format!("unknown tag '{}'", tag))?, tag => Err(format!("unknown tag '{}'", tag))?,
}) })
} }
@@ -406,6 +405,61 @@ impl Instruction {
}) })
} }
fn compare(v1: Value, v2: Value) -> Result<i64, Box<dyn Error>> {
use std::cmp::Ordering;
match v1 {
Value::Integer(i1) => match v2 {
Value::Integer(i2) => Ok(i1 - i2),
Value::Float(f2) => Ok(
match (i1 as f64)
.partial_cmp(&f2)
.ok_or("incompatible comparison values")?
{
Ordering::Less => -1,
Ordering::Equal => 0,
Ordering::Greater => 1,
},
),
_ => Err("incompatible comparison values")?,
},
Value::Float(f1) => match v2 {
Value::Integer(i2) => Ok(
match f1
.partial_cmp(&(i2 as f64))
.ok_or("incompatible comparison values")?
{
Ordering::Less => -1,
Ordering::Equal => 0,
Ordering::Greater => 1,
},
),
Value::Float(f2) => Ok(
match f1
.partial_cmp(&f2)
.ok_or("incompatible comparison values")?
{
Ordering::Less => -1,
Ordering::Equal => 0,
Ordering::Greater => 1,
},
),
_ => Err("incompatible comparison values")?,
},
Value::String(s1) => {
if let Value::String(s2) = v2 {
Ok(match s1.cmp(&s2) {
Ordering::Less => -1,
Ordering::Equal => 0,
Ordering::Greater => 1,
})
} else {
Err("incompatible comparison values")?
}
}
_ => Err("incompatible comparison values")?,
}
}
fn run_all( fn run_all(
ins: &Vec<Instruction>, ins: &Vec<Instruction>,
ctx: &mut Context, ctx: &mut Context,
@@ -469,6 +523,39 @@ impl Instruction {
.run(ctx)? .run(ctx)?
.ok_or("invalid child value in 'not' tag")? .ok_or("invalid child value in 'not' tag")?
.to_bool() .to_bool()
{
0
} else {
1
},
)),
Instruction::Equal(v1, v2) => Some(Value::Integer(
if Instruction::compare(
v1.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
v2.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
)? == 0
{
1
} else {
0
},
)),
Instruction::Greater(v1, v2) => Some(Value::Integer(
if Instruction::compare(
v1.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
v2.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
)? > 0
{
1
} else {
0
},
)),
Instruction::Lower(v1, v2) => Some(Value::Integer(
if Instruction::compare(
v1.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
v2.run(ctx)?.ok_or("invalid child value in 'equal' tag")?,
)? < 0
{ {
1 1
} else { } else {
@@ -483,6 +570,8 @@ impl Instruction {
.ok_or("invalid child function in 'call' tag")?; .ok_or("invalid child function in 'call' tag")?;
if let Value::Function(f) = fct_val { if let Value::Function(f) = fct_val {
f.run(vals, ctx)? f.run(vals, ctx)?
} else if let Value::StdFunction(f) = fct_val {
f(vals)?
} else { } else {
Err("invalid function")? Err("invalid function")?
} }
@@ -494,6 +583,8 @@ impl Instruction {
if let Value::Function(f) = fct_val { if let Value::Function(f) = fct_val {
let mut local = ctx.clone(); let mut local = ctx.clone();
f.run(vals, &mut local)? f.run(vals, &mut local)?
} else if let Value::StdFunction(f) = fct_val {
f(vals)?
} else { } else {
Err("invalid function")? Err("invalid function")?
} }
@@ -586,38 +677,6 @@ impl Instruction {
} }
None None
} }
Instruction::Print(ins) => {
match ins.run(ctx)?.ok_or("invalid child value in 'print' tag")? {
Value::Integer(i) => println!("{}", i),
Value::Float(f) => println!("{}", f),
Value::String(s) => println!("{}", s),
_ => Err("Unprintable value")?,
};
None
}
Instruction::AssignArray(array_ins, index_ins, value_ins) => {
if let Some(Value::Array(vec)) = array_ins.run(ctx)? {
if let Some(Value::Integer(index)) = index_ins.run(ctx)? {
vec.borrow_mut().insert(
index.try_into()?,
value_ins
.run(ctx)?
.ok_or("invalid 'value' value in 'assign-array' tag")?,
);
}
}
None
}
Instruction::InsertArray(array_ins, value_ins) => {
if let Some(Value::Array(vec)) = array_ins.run(ctx)? {
vec.borrow_mut().push(
value_ins
.run(ctx)?
.ok_or("invalid 'value' value in 'insert-array' tag")?,
);
}
None
}
} }
} else { } else {
None None
@@ -658,6 +717,7 @@ pub enum Value {
String(String), String(String),
Array(Rc<RefCell<Vec<Value>>>), Array(Rc<RefCell<Vec<Value>>>),
Function(Function), Function(Function),
StdFunction(fn(Vec<Value>) -> Result<Option<Value>, Box<dyn Error>>),
} }
impl Value { impl Value {
@@ -701,12 +761,165 @@ impl Context {
} }
} }
mod stl {
use super::*;
use std::io;
pub fn inject_all(ctx: &mut Context) {
ctx.assign(String::from("print"), Value::StdFunction(print));
ctx.assign(String::from("input"), Value::StdFunction(input));
ctx.assign(
String::from("string-split"),
Value::StdFunction(string_split),
);
ctx.assign(String::from("array-set"), Value::StdFunction(array_set));
ctx.assign(String::from("array-push"), Value::StdFunction(array_push));
ctx.assign(String::from("array-pop"), Value::StdFunction(array_pop));
ctx.assign(String::from("array-get"), Value::StdFunction(array_get));
ctx.assign(String::from("array-length"), Value::StdFunction(array_length));
}
fn print(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
if vals.len() == 1 {
match &vals[0] {
Value::Integer(i) => println!("{}", i),
Value::Float(f) => println!("{}", f),
Value::String(s) => println!("{}", s),
v => println!("{:?}", v)
// _ => Err("unprintable value")?,
};
Ok(Some(vals[0].clone()))
} else {
Err("bad argument count in call to 'print'")?
}
}
fn input(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
if vals.len() == 0 {
let mut line = String::new();
io::stdin().read_line(&mut line)?;
line.pop();
Ok(Some(Value::String(line)))
} else {
Err("bad argument count in call to 'input'")?
}
}
fn string_split(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
if vals.len() == 2 {
if let Value::String(s) = &vals[0] {
if let Value::String(d) = &vals[1] {
let mut v = s.split(d)
.map(|sub| Value::String(sub.to_string()))
.collect::<Vec<Value>>();
v.remove(0);
v.pop();
Ok(Some(Value::Array(Rc::new(RefCell::new(v)))))
} else {
Err("invalid delimiter string in call to 'string-split'")?
}
} else {
Err("invalid target string in call to 'string-split'")?
}
} else {
Err("bad argument count in call to 'string-split'")?
}
}
fn array_set(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
if vals.len() == 3 {
if let Value::Array(v) = &vals[0] {
if let Value::Integer(i) = &vals[1] {
let index: usize = (*i).try_into()?;
if v.borrow().len() > index {
v.borrow_mut()[index] = vals[2].clone();
Ok(None)
} else {
Err("index out of array in call to 'array-set'")?
}
} else {
Err("invalid index in call to 'array-set'")?
}
} else {
Err("invalid array in call to 'array-set'")?
}
} else {
Err("bad argument count in call to 'array-set'")?
}
}
fn array_push(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
if vals.len() == 2 {
if let Value::Array(v) = &vals[0] {
v.borrow_mut().push(vals[1].clone());
Ok(None)
} else {
Err("invalid array in call to 'array-push'")?
}
} else {
Err("bad argument count in call to 'array-push'")?
}
}
fn array_pop(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
if vals.len() == 1 {
if let Value::Array(v) = &vals[0] {
Ok(Some(
v.borrow_mut()
.pop()
.ok_or("empty array in call to 'array-pop'")?,
))
} else {
Err("invalid array in call to 'array-pop'")?
}
} else {
Err("bad argument count in call to 'array-pop'")?
}
}
fn array_get(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
if vals.len() == 2 {
if let Value::Array(v) = &vals[0] {
if let Value::Integer(i) = &vals[1] {
let index: usize = (*i).try_into()?;
if v.borrow().len() > index {
Ok(Some(v.borrow_mut()[index].clone()))
} else {
Err("index out of array in call to 'array-get'")?
}
} else {
Err("invalid index in call to 'array-get'")?
}
} else {
Err("invalid array in call to 'array-get'")?
}
} else {
Err("bad argument count in call to 'array-get'")?
}
}
fn array_length(vals: Vec<Value>) -> Result<Option<Value>, Box<dyn Error>> {
if vals.len() == 1 {
if let Value::Array(v) = &vals[0] {
Ok(Some(
Value::Integer(v.borrow().len() as i64)
))
} else {
Err("invalid array in call to 'array-length'")?
}
} else {
Err("bad argument count in call to 'array-length'")?
}
}
}
pub fn run(filename: &str) -> Result<(), Box<dyn Error>> { pub fn run(filename: &str) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(filename)?; let contents = fs::read_to_string(filename)?;
let doc = Document::parse(&contents)?; let doc = Document::parse(&contents)?;
let root = doc.root(); let root = doc.root();
let mut ctx = Context::new(None); let mut ctx = Context::new(None);
stl::inject_all(&mut ctx);
let main = root let main = root
.first_element_child() .first_element_child()