Skip to content

Commit 8432120

Browse files
committed
feat: Add stdlib builtins and break/continue control flow
New metabuiltins (13 total): - len: return length of strings, lists, or tuples - print_line: print with trailing newline - join: concatenate list of strings with separator - to_int / to_real: type conversion builtins - input_int / input_real: parse numeric input from stdin - to_string_fixed: format numbers with fixed decimal places - str_concat: explicit string concatenation Control flow: - Add break/continue as reserved keywords in parser - Add Statement::Break and Statement::Continue to AST - Add Computation::BreakLoop/ContinueLoop variants in interpreter - Handle break/continue in while/for loops (exit or skip iteration) - Type checker passes through break/continue as Ok(env) Type checker: - Add check_builtin_signature() for stdlib call validation - Validate arity and types for print, len, join, to_int, to_real, etc. Pretty printer: - Add formatting for break; and continue; statements Tests: - Update stdlib test to expect 13 metabuiltins - Add unit tests for len, join, to_int, to_real, to_string_fixed - Fix exhaustive match patterns for new Computation variants README v0.0.1: - Update README to reflect the project's current status (contents, capabilites, limitations...)
1 parent ca53cf8 commit 8432120

File tree

12 files changed

+1125
-197
lines changed

12 files changed

+1125
-197
lines changed

README.md

Lines changed: 256 additions & 146 deletions
Large diffs are not rendered by default.

src/interpreter/expression_eval.rs

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,10 @@ pub fn eval_function_call(
472472
Ok(Computation::Continue(_)) => Err("Function did not return a value".to_string()),
473473
Ok(Computation::Return(value, _final_env)) => Ok(ExpressionResult::Value(value)),
474474
Ok(Computation::PropagateError(value, _)) => Ok(ExpressionResult::Propagate(value)),
475+
Ok(Computation::BreakLoop(_)) => Err("'break' used outside of a loop".to_string()),
476+
Ok(Computation::ContinueLoop(_)) => {
477+
Err("'continue' used outside of a loop".to_string())
478+
}
475479
Err(e) => Err(e),
476480
}
477481
}
@@ -524,6 +528,256 @@ pub fn eval_function_call(
524528
Ok(ExpressionResult::Value(Expression::CVoid))
525529
}
526530

531+
// input_int([prompt]) -> Integer or error string
532+
"input_int" => {
533+
if let Some(prompt_expr) = args.get(0) {
534+
let prompt_val = match eval(prompt_expr.clone(), env)? {
535+
ExpressionResult::Value(Expression::CString(s)) => {
536+
Expression::CString(s)
537+
}
538+
ExpressionResult::Value(v) => {
539+
Expression::CString(format!("{:?}", v))
540+
}
541+
ExpressionResult::Propagate(e) => {
542+
return Ok(ExpressionResult::Propagate(e))
543+
}
544+
};
545+
meta_env.map_variable("prompt".to_string(), false, prompt_val);
546+
}
547+
548+
let stmt = meta_fn(&mut meta_env);
549+
if let Statement::Return(expr) = stmt {
550+
Ok(ExpressionResult::Value(*expr))
551+
} else {
552+
Err("[Runtime Error] input_int builtin did not return a value".into())
553+
}
554+
}
555+
556+
// input_real([prompt]) -> Real or error string
557+
"input_real" => {
558+
if let Some(prompt_expr) = args.get(0) {
559+
let prompt_val = match eval(prompt_expr.clone(), env)? {
560+
ExpressionResult::Value(Expression::CString(s)) => {
561+
Expression::CString(s)
562+
}
563+
ExpressionResult::Value(v) => {
564+
Expression::CString(format!("{:?}", v))
565+
}
566+
ExpressionResult::Propagate(e) => {
567+
return Ok(ExpressionResult::Propagate(e))
568+
}
569+
};
570+
meta_env.map_variable("prompt".to_string(), false, prompt_val);
571+
}
572+
573+
let stmt = meta_fn(&mut meta_env);
574+
if let Statement::Return(expr) = stmt {
575+
Ok(ExpressionResult::Value(*expr))
576+
} else {
577+
Err("[Runtime Error] input_real builtin did not return a value".into())
578+
}
579+
}
580+
581+
// to_string(value) -> String
582+
"to_string" => {
583+
if args.len() != 1 {
584+
return Err(
585+
"[Runtime Error] to_string expects exactly 1 argument".into()
586+
);
587+
}
588+
589+
let val = match eval(args[0].clone(), env)? {
590+
ExpressionResult::Value(v) => v,
591+
ExpressionResult::Propagate(e) => {
592+
return Ok(ExpressionResult::Propagate(e))
593+
}
594+
};
595+
meta_env.map_variable("value".to_string(), false, val);
596+
let stmt = meta_fn(&mut meta_env);
597+
if let Statement::Return(expr) = stmt {
598+
Ok(ExpressionResult::Value(*expr))
599+
} else {
600+
Err("[Runtime Error] to_string builtin did not return a value".into())
601+
}
602+
}
603+
604+
// len(value) -> Int or error string
605+
"len" => {
606+
if args.len() != 1 {
607+
return Err("[Runtime Error] len expects exactly 1 argument".into());
608+
}
609+
610+
let val = match eval(args[0].clone(), env)? {
611+
ExpressionResult::Value(v) => v,
612+
ExpressionResult::Propagate(e) => {
613+
return Ok(ExpressionResult::Propagate(e))
614+
}
615+
};
616+
617+
meta_env.map_variable("value".to_string(), false, val);
618+
let stmt = meta_fn(&mut meta_env);
619+
if let Statement::Return(expr) = stmt {
620+
Ok(ExpressionResult::Value(*expr))
621+
} else {
622+
Err("[Runtime Error] len builtin did not return a value".into())
623+
}
624+
}
625+
626+
// print_line(value) -> Unit
627+
"print_line" => {
628+
if args.len() != 1 {
629+
return Err(
630+
"[Runtime Error] print_line expects exactly 1 argument".into()
631+
);
632+
}
633+
let val = match eval(args[0].clone(), env)? {
634+
ExpressionResult::Value(v) => v,
635+
ExpressionResult::Propagate(e) => {
636+
return Ok(ExpressionResult::Propagate(e))
637+
}
638+
};
639+
meta_env.map_variable("value".to_string(), false, val);
640+
let _ = meta_fn(&mut meta_env);
641+
Ok(ExpressionResult::Value(Expression::CVoid))
642+
}
643+
644+
// join(values, sep) -> String
645+
"join" => {
646+
if args.len() != 2 {
647+
return Err("[Runtime Error] join expects exactly 2 arguments".into());
648+
}
649+
650+
let values_val = match eval(args[0].clone(), env)? {
651+
ExpressionResult::Value(v) => v,
652+
ExpressionResult::Propagate(e) => {
653+
return Ok(ExpressionResult::Propagate(e))
654+
}
655+
};
656+
let sep_val = match eval(args[1].clone(), env)? {
657+
ExpressionResult::Value(v) => v,
658+
ExpressionResult::Propagate(e) => {
659+
return Ok(ExpressionResult::Propagate(e))
660+
}
661+
};
662+
663+
meta_env.map_variable("values".to_string(), false, values_val);
664+
meta_env.map_variable("sep".to_string(), false, sep_val);
665+
let stmt = meta_fn(&mut meta_env);
666+
if let Statement::Return(expr) = stmt {
667+
Ok(ExpressionResult::Value(*expr))
668+
} else {
669+
Err("[Runtime Error] join builtin did not return a value".into())
670+
}
671+
}
672+
673+
// to_int(value) -> Int or error string
674+
"to_int" => {
675+
if args.len() != 1 {
676+
return Err("[Runtime Error] to_int expects exactly 1 argument".into());
677+
}
678+
679+
let val = match eval(args[0].clone(), env)? {
680+
ExpressionResult::Value(v) => v,
681+
ExpressionResult::Propagate(e) => {
682+
return Ok(ExpressionResult::Propagate(e))
683+
}
684+
};
685+
meta_env.map_variable("value".to_string(), false, val);
686+
let stmt = meta_fn(&mut meta_env);
687+
if let Statement::Return(expr) = stmt {
688+
Ok(ExpressionResult::Value(*expr))
689+
} else {
690+
Err("[Runtime Error] to_int builtin did not return a value".into())
691+
}
692+
}
693+
694+
// to_real(value) -> Real or error string
695+
"to_real" => {
696+
if args.len() != 1 {
697+
return Err("[Runtime Error] to_real expects exactly 1 argument".into());
698+
}
699+
700+
let val = match eval(args[0].clone(), env)? {
701+
ExpressionResult::Value(v) => v,
702+
ExpressionResult::Propagate(e) => {
703+
return Ok(ExpressionResult::Propagate(e))
704+
}
705+
};
706+
meta_env.map_variable("value".to_string(), false, val);
707+
let stmt = meta_fn(&mut meta_env);
708+
if let Statement::Return(expr) = stmt {
709+
Ok(ExpressionResult::Value(*expr))
710+
} else {
711+
Err("[Runtime Error] to_real builtin did not return a value".into())
712+
}
713+
}
714+
715+
// to_string_fixed(value, places) -> String
716+
"to_string_fixed" => {
717+
if args.len() != 2 {
718+
return Err(
719+
"[Runtime Error] to_string_fixed expects exactly 2 arguments"
720+
.into(),
721+
);
722+
}
723+
724+
let val = match eval(args[0].clone(), env)? {
725+
ExpressionResult::Value(v) => v,
726+
ExpressionResult::Propagate(e) => {
727+
return Ok(ExpressionResult::Propagate(e))
728+
}
729+
};
730+
let places = match eval(args[1].clone(), env)? {
731+
ExpressionResult::Value(v) => v,
732+
ExpressionResult::Propagate(e) => {
733+
return Ok(ExpressionResult::Propagate(e))
734+
}
735+
};
736+
737+
meta_env.map_variable("value".to_string(), false, val);
738+
meta_env.map_variable("places".to_string(), false, places);
739+
let stmt = meta_fn(&mut meta_env);
740+
if let Statement::Return(expr) = stmt {
741+
Ok(ExpressionResult::Value(*expr))
742+
} else {
743+
Err(
744+
"[Runtime Error] to_string_fixed builtin did not return a value"
745+
.into(),
746+
)
747+
}
748+
}
749+
750+
// str_concat(left, right) -> String
751+
"str_concat" => {
752+
if args.len() != 2 {
753+
return Err(
754+
"[Runtime Error] str_concat expects exactly 2 arguments".into()
755+
);
756+
}
757+
758+
let left_val = match eval(args[0].clone(), env)? {
759+
ExpressionResult::Value(v) => v,
760+
ExpressionResult::Propagate(e) => {
761+
return Ok(ExpressionResult::Propagate(e))
762+
}
763+
};
764+
let right_val = match eval(args[1].clone(), env)? {
765+
ExpressionResult::Value(v) => v,
766+
ExpressionResult::Propagate(e) => {
767+
return Ok(ExpressionResult::Propagate(e))
768+
}
769+
};
770+
771+
meta_env.map_variable("left".to_string(), false, left_val);
772+
meta_env.map_variable("right".to_string(), false, right_val);
773+
let stmt = meta_fn(&mut meta_env);
774+
if let Statement::Return(expr) = stmt {
775+
Ok(ExpressionResult::Value(*expr))
776+
} else {
777+
Err("[Runtime Error] str_concat builtin did not return a value".into())
778+
}
779+
}
780+
527781
// open(path, [mode], [content]) -> String ou Unit
528782
"open" => {
529783
if args.is_empty() {

0 commit comments

Comments
 (0)