mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: Implement primitive casting.
This commit is contained in:
parent
392050d9f4
commit
632019ac18
@ -81,3 +81,33 @@ assert t.ok{
|
||||
test = foo_string in ["foo"],
|
||||
desc = "List presence checks work",
|
||||
};
|
||||
|
||||
assert t.ok{
|
||||
test = int("1") == 1,
|
||||
desc = "You can cast a string into an int",
|
||||
};
|
||||
|
||||
assert t.ok{
|
||||
test = str(1) == "1",
|
||||
desc = "You can cast an int into a string",
|
||||
};
|
||||
|
||||
assert t.ok{
|
||||
test = bool("true") == true,
|
||||
desc = "You can cast an string into true",
|
||||
};
|
||||
|
||||
assert t.ok{
|
||||
test = bool("false") == false,
|
||||
desc = "You can cast a string into false",
|
||||
};
|
||||
|
||||
assert t.ok{
|
||||
test = str(true) == "true",
|
||||
desc = "You can cast true into a string",
|
||||
};
|
||||
|
||||
assert t.ok{
|
||||
test = str(false) == "false",
|
||||
desc = "You can cast true into a string",
|
||||
};
|
@ -318,6 +318,38 @@ pub struct CallDef {
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
/// The allowable types to which you can perform a primitive cast.
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum CastType {
|
||||
Int,
|
||||
Float,
|
||||
Str,
|
||||
Bool,
|
||||
}
|
||||
|
||||
impl fmt::Display for CastType {
|
||||
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
w,
|
||||
"{}",
|
||||
match self {
|
||||
CastType::Int => "int",
|
||||
CastType::Float => "float",
|
||||
CastType::Bool => "bool",
|
||||
CastType::Str => "str",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a cast of a target to a primitive type.
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct CastDef {
|
||||
pub cast_type: CastType,
|
||||
pub target: Box<Expression>,
|
||||
pub pos: Position,
|
||||
}
|
||||
|
||||
/// Encodes a select expression in the UCG AST.
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct SelectDef {
|
||||
@ -694,6 +726,7 @@ pub enum Expression {
|
||||
Include(IncludeDef),
|
||||
Import(ImportDef),
|
||||
Call(CallDef),
|
||||
Cast(CastDef),
|
||||
Func(FuncDef),
|
||||
Select(SelectDef),
|
||||
FuncOp(FuncOpDef),
|
||||
@ -716,6 +749,7 @@ impl Expression {
|
||||
&Expression::Grouped(_, ref pos) => pos,
|
||||
&Expression::Format(ref def) => &def.pos,
|
||||
&Expression::Call(ref def) => &def.pos,
|
||||
&Expression::Cast(ref def) => &def.pos,
|
||||
&Expression::Func(ref def) => &def.pos,
|
||||
&Expression::Module(ref def) => &def.pos,
|
||||
&Expression::Select(ref def) => &def.pos,
|
||||
@ -754,7 +788,10 @@ impl fmt::Display for Expression {
|
||||
write!(w, "<Format Expr>")?;
|
||||
}
|
||||
&Expression::Call(_) => {
|
||||
write!(w, "<MacroCall>")?;
|
||||
write!(w, "<FuncCall>")?;
|
||||
}
|
||||
&Expression::Cast(_) => {
|
||||
write!(w, "<Cast>")?;
|
||||
}
|
||||
&Expression::Func(_) => {
|
||||
write!(w, "<Func>")?;
|
||||
|
@ -258,6 +258,13 @@ where
|
||||
}
|
||||
self.render_expr(&_def.right)?;
|
||||
}
|
||||
Expression::Cast(def) => {
|
||||
self.w.write(format!("{}", def.cast_type).as_bytes())?;
|
||||
self.w.write("(".as_bytes())?;
|
||||
self.render_comment_if_needed(def.target.pos().line)?;
|
||||
self.render_expr(&def.target)?;
|
||||
self.w.write(")".as_bytes())?;
|
||||
}
|
||||
Expression::Call(_def) => {
|
||||
self.render_value(&_def.funcref)?;
|
||||
self.w.write("(".as_bytes())?;
|
||||
|
@ -42,6 +42,9 @@ pub trait Walker {
|
||||
self.walk_expression(expr);
|
||||
}
|
||||
}
|
||||
Expression::Cast(ref mut def) => {
|
||||
self.walk_expression(&mut def.target);
|
||||
}
|
||||
Expression::Copy(ref mut def) => {
|
||||
self.walk_fieldset(&mut def.fields);
|
||||
}
|
||||
|
@ -17,28 +17,28 @@ use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::env;
|
||||
use std::process;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
use std::rc::Rc;
|
||||
|
||||
use rustyline;
|
||||
use rustyline::error::ReadlineError;
|
||||
use atty;
|
||||
use atty::Stream;
|
||||
use rustyline;
|
||||
use rustyline::error::ReadlineError;
|
||||
use simple_error;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::error;
|
||||
use crate::iter::OffsetStrIter;
|
||||
use crate::parse::parse;
|
||||
use crate::build::opcode::pointer::OpPointer;
|
||||
use crate::build::opcode::translate;
|
||||
use crate::build::opcode::translate::PositionMap;
|
||||
use crate::build::opcode::Environment;
|
||||
use crate::build::opcode::VM;
|
||||
use crate::error;
|
||||
use crate::iter::OffsetStrIter;
|
||||
use crate::parse::parse;
|
||||
|
||||
pub mod format;
|
||||
pub mod ir;
|
||||
@ -216,12 +216,12 @@ where
|
||||
|
||||
pub fn repl(&mut self, mut editor: rustyline::Editor<()>, config_home: PathBuf) -> BuildResult {
|
||||
// loop
|
||||
let mut lines = crate::io::StatementAccumulator::new();
|
||||
if atty::is(Stream::Stdin) {
|
||||
println!("Welcome to the UCG repl. Ctrl-D to exit, Ctrl-C to abort expression.");
|
||||
println!("Type '#help' for help.");
|
||||
println!("");
|
||||
}
|
||||
let mut lines = crate::io::StatementAccumulator::new();
|
||||
if atty::is(Stream::Stdin) {
|
||||
println!("Welcome to the UCG repl. Ctrl-D to exit, Ctrl-C to abort expression.");
|
||||
println!("Type '#help' for help.");
|
||||
println!("");
|
||||
}
|
||||
// Initialize VM with an empty OpPointer
|
||||
let mut vm = VM::new(
|
||||
self.strict,
|
||||
|
93
src/build/opcode/convert.rs
Normal file
93
src/build/opcode/convert.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use super::Primitive;
|
||||
use crate::ast::CastType;
|
||||
|
||||
pub struct Error {
|
||||
val: Primitive,
|
||||
cast_type: CastType,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn message(&self) -> String {
|
||||
format!("No cast from {} to {}", self.val, self.cast_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Primitive> for String {
|
||||
fn from(p: &Primitive) -> Self {
|
||||
match p {
|
||||
Primitive::Int(i) => format!("{}", i),
|
||||
Primitive::Float(f) => format!("{}", f),
|
||||
Primitive::Str(s) => format!("{}", s),
|
||||
Primitive::Bool(b) => format!("{}", b),
|
||||
Primitive::Empty => "NULL".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Primitive> for i64 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(p: &Primitive) -> Result<Self, Self::Error> {
|
||||
match p {
|
||||
Primitive::Bool(_) | Primitive::Empty => Err(Error {
|
||||
val: p.clone(),
|
||||
cast_type: CastType::Int,
|
||||
}),
|
||||
Primitive::Str(s) => match s.parse::<i64>() {
|
||||
Ok(i) => Ok(i),
|
||||
Err(_) => Err(Error {
|
||||
val: Primitive::Str(s.clone()),
|
||||
cast_type: CastType::Int,
|
||||
}),
|
||||
},
|
||||
Primitive::Float(f) => Ok(*f as i64),
|
||||
Primitive::Int(i) => Ok(i.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Primitive> for f64 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(p: &Primitive) -> Result<Self, Self::Error> {
|
||||
match p {
|
||||
Primitive::Bool(_) | Primitive::Empty => Err(Error {
|
||||
val: p.clone(),
|
||||
cast_type: CastType::Int,
|
||||
}),
|
||||
Primitive::Str(s) => match s.parse::<f64>() {
|
||||
Ok(f) => Ok(f),
|
||||
Err(_) => Err(Error {
|
||||
val: Primitive::Str(s.clone()),
|
||||
cast_type: CastType::Int,
|
||||
}),
|
||||
},
|
||||
Primitive::Int(i) => Ok(*i as f64),
|
||||
Primitive::Float(f) => Ok(f.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Primitive> for bool {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(p: &Primitive) -> Result<Self, Self::Error> {
|
||||
match p {
|
||||
Primitive::Empty | Primitive::Int(_) | Primitive::Float(_) => Err(Error {
|
||||
val: p.clone(),
|
||||
cast_type: CastType::Int,
|
||||
}),
|
||||
Primitive::Bool(b) => Ok(*b),
|
||||
Primitive::Str(s) => match s.as_str() {
|
||||
"true" => Ok(true),
|
||||
"false" => Ok(false),
|
||||
_ => Err(Error {
|
||||
val: Primitive::Str(s.clone()),
|
||||
cast_type: CastType::Int,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -24,11 +24,7 @@ use Value::{C, F, M, P, S, T};
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
P(Bool(v)) => write!(w, "{}", v),
|
||||
P(Int(v)) => write!(w, "{}", v),
|
||||
P(Float(v)) => write!(w, "{}", v),
|
||||
P(Str(v)) => write!(w, "\"{}\"", v.replace("\"", "\\\"")),
|
||||
P(Empty) => write!(w, "NULL"),
|
||||
P(p) => write!(w, "{}", p),
|
||||
C(List(ref els, _)) => {
|
||||
write!(w, "[")?;
|
||||
for e in els {
|
||||
@ -50,3 +46,15 @@ impl fmt::Display for Value {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Primitive {
|
||||
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Bool(v) => write!(w, "{}", v),
|
||||
Int(v) => write!(w, "{}", v),
|
||||
Float(v) => write!(w, "{}", v),
|
||||
Str(v) => write!(w, "\"{}\"", v.replace("\"", "\\\"")),
|
||||
Empty => write!(w, "NULL"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ use std::fmt::Display;
|
||||
use std::io;
|
||||
|
||||
use crate::ast::Position;
|
||||
use crate::build::opcode::convert;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
@ -91,6 +92,16 @@ impl From<std::io::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<convert::Error> for Error {
|
||||
fn from(e: convert::Error) -> Self {
|
||||
Error {
|
||||
message: e.message(),
|
||||
pos: None,
|
||||
call_stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(ref pos) = self.pos {
|
||||
|
@ -19,6 +19,7 @@ mod display;
|
||||
pub mod environment;
|
||||
#[macro_use]
|
||||
mod error;
|
||||
mod convert;
|
||||
pub mod pointer;
|
||||
mod runtime;
|
||||
pub mod scope;
|
||||
@ -29,11 +30,10 @@ pub use environment::Environment;
|
||||
pub use error::Error;
|
||||
pub use vm::VM;
|
||||
|
||||
use crate::ast::{CastType, Position};
|
||||
use pointer::OpPointer;
|
||||
use scope::Stack;
|
||||
|
||||
use crate::ast::Position;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Primitive {
|
||||
// Primitive Types
|
||||
@ -64,18 +64,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Primitive> for String {
|
||||
fn from(p: &Primitive) -> Self {
|
||||
match p {
|
||||
Int(i) => format!("{}", i),
|
||||
Float(f) => format!("{}", f),
|
||||
Str(s) => format!("{}", s),
|
||||
Bool(b) => format!("{}", b),
|
||||
Empty => "NULL".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Composite {
|
||||
List(Vec<Rc<Value>>, Vec<Position>),
|
||||
@ -198,6 +186,8 @@ pub enum Op {
|
||||
Not,
|
||||
// Primitive Types ops
|
||||
Val(Primitive),
|
||||
// Primitive casts
|
||||
Cast(CastType),
|
||||
// A bareword for use in bindings or lookups
|
||||
Sym(String),
|
||||
// Reference a binding on the heap
|
||||
@ -274,6 +264,7 @@ impl PartialEq for Value {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jwall): Move all of this into the convert module.
|
||||
impl From<Rc<Value>> for Val {
|
||||
fn from(val: Rc<Value>) -> Val {
|
||||
val.as_ref().into()
|
||||
|
@ -530,6 +530,10 @@ impl AST {
|
||||
Self::translate_value(call_def.funcref, &mut ops, root);
|
||||
ops.push(Op::FCall, func_pos);
|
||||
}
|
||||
Expression::Cast(cast_def) => {
|
||||
Self::translate_expr(*cast_def.target, &mut ops, root);
|
||||
ops.push(Op::Cast(cast_def.cast_type), cast_def.pos);
|
||||
}
|
||||
Expression::Copy(def) => {
|
||||
Self::translate_value(def.selector, &mut ops, root);
|
||||
Self::translate_copy(ops, def.fields, def.pos, root);
|
||||
|
@ -13,10 +13,11 @@
|
||||
// limitations under the License.
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeSet;
|
||||
use std::convert::TryInto;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::ast::Position;
|
||||
use crate::ast::{CastType, Position};
|
||||
|
||||
use super::environment::Environment;
|
||||
use super::pointer::OpPointer;
|
||||
@ -158,6 +159,7 @@ where
|
||||
let idx = self.ops.idx()?;
|
||||
match op {
|
||||
Op::Val(p) => self.push(Rc::new(P(p.clone())), pos)?,
|
||||
Op::Cast(t) => self.op_cast(t)?,
|
||||
Op::Sym(s) => self.push(Rc::new(S(s.clone())), pos)?,
|
||||
Op::DeRef(s) => self.op_deref(s.clone(), &pos)?,
|
||||
Op::Add => self.op_add(pos)?,
|
||||
@ -219,6 +221,22 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_cast(&mut self, t: CastType) -> Result<(), Error> {
|
||||
let (val, pos) = self.pop()?;
|
||||
if let Value::P(ref p) = val.as_ref() {
|
||||
self.push(
|
||||
Rc::new(match t {
|
||||
CastType::Str => Value::P(Primitive::Str(format!("{}", p))),
|
||||
CastType::Int => Value::P(Primitive::Int(p.try_into()?)),
|
||||
CastType::Float => Value::P(Primitive::Float(p.try_into()?)),
|
||||
CastType::Bool => Value::P(Primitive::Bool(p.try_into()?)),
|
||||
}),
|
||||
pos,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_typ(&mut self) -> Result<(), Error> {
|
||||
let (val, pos) = self.pop()?;
|
||||
let typ_name = match val.as_ref() {
|
||||
|
@ -538,6 +538,32 @@ fn tuple_to_call<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
make_fn!(
|
||||
cast_expression<SliceIter<Token>, Expression>,
|
||||
do_each!(
|
||||
typ => either!(
|
||||
word!("int"),
|
||||
word!("float"),
|
||||
word!("str"),
|
||||
word!("bool")
|
||||
),
|
||||
_ => punct!("("),
|
||||
expr => expression,
|
||||
_ => punct!(")"),
|
||||
(Expression::Cast(CastDef{
|
||||
cast_type: match typ.fragment.as_str() {
|
||||
"int" => CastType::Int,
|
||||
"float" => CastType::Float,
|
||||
"str" => CastType::Str,
|
||||
"bool" => CastType::Bool,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
target: Box::new(expr),
|
||||
pos: typ.pos,
|
||||
}))
|
||||
)
|
||||
);
|
||||
|
||||
fn call_expression(input: SliceIter<Token>) -> Result<SliceIter<Token>, Expression> {
|
||||
let parsed = do_each!(input.clone(),
|
||||
callee_name => trace_parse!(symbol),
|
||||
@ -717,6 +743,8 @@ fn unprefixed_expression(input: SliceIter<Token>) -> ParseResult<Expression> {
|
||||
trace_parse!(format_expression),
|
||||
trace_parse!(range_expression),
|
||||
trace_parse!(simple_expression),
|
||||
// cast parse attempts must happen before call parse attempts.
|
||||
trace_parse!(cast_expression),
|
||||
trace_parse!(call_expression),
|
||||
trace_parse!(copy_expression)
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user