Skip to content

Commit

Permalink
Merge pull request #520 from shiika-lang/try-bang2
Browse files Browse the repository at this point in the history
impl. Result#try!
  • Loading branch information
yhara authored Nov 9, 2023
2 parents b3dc450 + 6eea098 commit f998881
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 110 deletions.
12 changes: 4 additions & 8 deletions builtin/file.sk
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@ class File : Readable
# def self.write(path: String, content: String) -> Result<Void>

def self.open<V>(path: String, f: Fn1<File,V>) -> Result<V>
match File._open(path)
when Ok(file)
let v = f(file)
#file.close
Ok.new(v)
when Fail(e)
Fail.new(e)
end
let file = File._open(path).try!
let v = f(file)
#file.close
Ok.new(v)
end

# TODO: will we need this?
Expand Down
40 changes: 16 additions & 24 deletions builtin/readable.sk
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,19 @@ module Readable
let l = MutableString.new
var done = false
while true
match fill_buf
when Ok(s)
if s.bytesize == 0
done = true
else
s.each_byte do |b|
if b == 10 # LF(\n)
consume(1)
done = true
break
end
l.append_byte(b)
let s = fill_buf.try!
if s.bytesize == 0
done = true
else
s.each_byte do |b|
if b == 10 # LF(\n)
consume(1)
done = true
break
end
l.append_byte(b)
consume(1)
end
when Fail(e)
return Fail.new(e)
end
break if done
end
Expand Down Expand Up @@ -55,16 +51,12 @@ module Readable
def read -> Result<String>
let acc = MutableString.new
while true
match fill_buf
when Ok(s)
if s.bytesize == 0
break
else
acc.append(s)
consume(s.bytesize)
end
when Fail(e)
return Fail.new(e)
let s = fill_buf.try!
if s.bytesize == 0
break
else
acc.append(s)
consume(s.bytesize)
end
end
Ok.new(acc._unsafe_to_s)
Expand Down
7 changes: 7 additions & 0 deletions builtin/result.sk
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ enum Result<V>
end
end

# Special method which returns the `value` if this is `Ok` and otherwise escapes the
# current method like `return self`.
def try! # -> V or Never
# Call to this method is replaced with a pattern match in
# skc_ast2hir.
end

def unwrap -> V
match self
when Ok(v) then v
Expand Down
2 changes: 2 additions & 0 deletions lib/shiika_parser/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ impl<'a> Lexer<'a> {
// Read either of
// - an identifier starting with a small letter
// - May be suffixed by a `?`
// - May be suffixed by a `!`
// - May be suffixed by a `=` when lexer state is LexerState::MethodName.
// - a keyword (`if`, `class`, etc.)
// - a KeyName (`foo:`, etc.).
Expand All @@ -369,6 +370,7 @@ impl<'a> Lexer<'a> {
next_cur.proceed(self.src);
return (Token::KeyName(s.to_string()), Some(LexerState::ExprBegin));
} else if c == Some('?')
|| c == Some('!')
|| (c == Some('=') && self.state == LexerState::MethodName)
{
next_cur.proceed(self.src);
Expand Down
31 changes: 11 additions & 20 deletions lib/skc_ast2hir/src/convert_exprs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,7 @@ impl<'hir_maker> HirMaker<'hir_maker> {
clauses: &[AstMatchClause],
_locs: &LocationSpan,
) -> Result<HirExpression> {
let (match_expr, lvars) = pattern_match::convert_match_expr(self, cond_expr, clauses)?;
for lvar in lvars {
let readonly = true;
self.ctx_stack.declare_lvar(&lvar.name, lvar.ty, readonly);
}
Ok(match_expr)
pattern_match::convert_match_expr(self, cond_expr, clauses)
}

fn convert_while_expr(
Expand Down Expand Up @@ -338,10 +333,8 @@ impl<'hir_maker> HirMaker<'hir_maker> {
)
};
let merge_ty = self._validate_return_type(&arg_expr.ty, locs)?;
Ok(Hir::bit_cast(
merge_ty,
Hir::return_expression(from, arg_expr, locs.clone()),
))
let cast = Hir::bit_cast(merge_ty, arg_expr);
Ok(Hir::return_expression(from, cast, locs.clone()))
}

/// Check if `return' is valid in the current context
Expand Down Expand Up @@ -656,16 +649,14 @@ impl<'hir_maker> HirMaker<'hir_maker> {
return Ok(lvar_info.ref_expr());
}

// Search method
let self_expr = self.convert_self_expr(&LocationSpan::todo());
let result = self
.class_dict
.lookup_method(&self_expr.ty, &method_firstname(name), locs);
if let Ok(found) = result {
method_call::build_simple(self, found, self_expr, locs)
} else {
Err(error::unknown_barename(name, locs))
}
method_call::convert_method_call(
self,
&None,
&method_firstname(name),
&Default::default(),
Default::default(),
locs,
)
}

/// Return the variable of the given name, if any
Expand Down
53 changes: 13 additions & 40 deletions lib/skc_ast2hir/src/convert_exprs/method_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::class_dict::FoundMethod;
use crate::convert_exprs::{block, block::BlockTaker};
use crate::error;
use crate::hir_maker::HirMaker;
use crate::pattern_match;
use crate::type_inference::Infer;
use crate::type_system::type_checking;
use anyhow::{Context, Result};
Expand Down Expand Up @@ -99,6 +100,8 @@ pub fn convert_method_call(
tyargs,
locs,
));
} else if found.sig.fullname.full_name == "Result#try!" {
return expand_result_try(mk, receiver_hir, locs);
}

let ret_ty = inf.ret_ty().with_context(|| error(&block_taker, locs))?;
Expand Down Expand Up @@ -257,46 +260,6 @@ fn convert_method_args(
Ok(arg_hirs)
}

/// For method calls without any arguments.
pub fn build_simple(
mk: &mut HirMaker,
found: FoundMethod,
receiver_hir: HirExpression,
locs: &LocationSpan,
) -> Result<HirExpression> {
debug_assert!(!found.sig.has_typarams());
let receiver_ty = &receiver_hir.ty;

let block_taker = BlockTaker::Method {
sig: found.sig.clone(),
locs,
};
let class_typarams = &mk
.class_dict
.get_type(&found.owner.to_type_fullname())
.base()
.typarams;
let class_tyargs = receiver_ty.type_args();
let inf = Infer::new(
&block_taker,
class_typarams,
class_tyargs,
Default::default(),
);
let ret_ty = inf.ret_ty()?;

let receiver = Hir::bit_cast(found.owner.to_term_ty(), receiver_hir);
let hir = build_hir(
mk,
&found,
receiver,
Default::default(),
Default::default(),
ret_ty,
);
Ok(hir)
}

fn check_argument_types(
mk: &HirMaker,
sig: &MethodSignature,
Expand Down Expand Up @@ -399,3 +362,13 @@ fn error(block_taker: &BlockTaker, locs: &LocationSpan) -> String {
};
error::method_call_tyinf_failed(detail, locs).to_string()
}

// match receiver when Ok(v) then v when e then return e
fn expand_result_try(
mk: &mut HirMaker,
receiver_hir: HirExpression,
locs: &LocationSpan,
) -> Result<HirExpression> {
debug_assert!(receiver_hir.ty.fullname.0.starts_with("Result"));
pattern_match::expand_result_try(mk, receiver_hir, locs)
}
22 changes: 14 additions & 8 deletions lib/skc_ast2hir/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,6 @@ pub fn method_not_found(msg: String, locs: &LocationSpan) -> anyhow::Error {
program_error(report)
}

pub fn unknown_barename(name: &str, locs: &LocationSpan) -> anyhow::Error {
let msg = format!("variable or method `{}' was not found", name);
let report = skc_error::build_report(msg.clone(), locs, |r, locs_span| {
r.with_label(Label::new(locs_span).with_message(msg))
});
program_error(report)
}

pub fn unspecified_arg(
param_name: &str,
sig: &MethodSignature,
Expand Down Expand Up @@ -162,3 +154,17 @@ pub fn if_clauses_type_mismatch(
.build(main_msg, &then_locs);
type_error(report)
}

pub fn match_clauses_type_mismatch(
ty1: &TermTy,
locs1: LocationSpan,
ty2: &TermTy,
locs2: LocationSpan,
) -> anyhow::Error {
let main_msg = format!("match clauses type mismatch ({} vs {})", &ty1, &ty2);
let report = skc_error::report_builder()
.annotate(locs1.clone(), ty1.to_string())
.annotate(locs2, ty2.to_string())
.build(main_msg, &locs1);
type_error(report)
}
Loading

0 comments on commit f998881

Please sign in to comment.