Skip to content

Commit

Permalink
Merge pull request #246 from calcit-lang/edn-strict
Browse files Browse the repository at this point in the history
new macro record-with with proc &record:with
  • Loading branch information
NoEgAm committed Jun 24, 2024
2 parents 6c86e4e + 994a864 commit 9b9f080
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "calcit"
version = "0.8.57"
version = "0.8.58"
authors = ["jiyinyiyong <[email protected]>"]
edition = "2021"
license = "MIT"
Expand Down
15 changes: 14 additions & 1 deletion calcit/test-record.cirru
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
def Lagopus $ new-class-record BirdClass :Lagopus :name
|main! $ %{} :CodeEntry (:doc |)
:code $ quote
defn main! () (test-record) (test-methods) (test-match) (test-polymorphism) (test-edn) (do true)
defn main! () (test-record) (test-methods) (test-match) (test-polymorphism) (test-edn) (test-record-with) (do true)
|test-edn $ %{} :CodeEntry (:doc |)
:code $ quote
fn ()
Expand Down Expand Up @@ -95,6 +95,19 @@
-> l1 (.rename |LagopusB) (.show)
assert= (&record:class l1)
&record:class $ &record:with-class a1 BirdClass
|test-record-with $ %{} :CodeEntry (:doc "|test record-with")
:code $ quote
fn () (log-title "|Testing record-with")
let
Person $ new-record :City :name :age :position
p1 $ %{} Person (:name |Chen) (:age 20) (:position :hangzhou)
p2 $ record-with p1 (:age 21) (:position :shanghai)
; println |P2 p2
assert= 20 $ get p1 :age
assert= 21 $ get p2 :age
assert= :hangzhou $ get p1 :position
assert= :shanghai $ get p2 :position
assert= |Chen $ get p2 :name
|test-record $ %{} :CodeEntry (:doc |)
:code $ quote
fn () (log-title "|Testing record")
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@calcit/procs",
"version": "0.8.57",
"version": "0.8.58",
"main": "./lib/calcit.procs.mjs",
"devDependencies": {
"@types/node": "^20.11.28",
Expand Down
1 change: 1 addition & 0 deletions src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ fn handle_proc_internal(name: CalcitProc, args: &[Calcit], call_stack: &CallStac
CalcitProc::NewRecord => records::new_record(args),
CalcitProc::NewClassRecord => records::new_class_record(args),
CalcitProc::NativeRecord => records::call_record(args),
CalcitProc::NativeRecordWith => records::record_with(args),
CalcitProc::NativeRecordClass => records::get_class(args),
CalcitProc::NativeRecordWithClass => records::with_class(args),
CalcitProc::NativeRecordFromMap => records::record_from_map(args),
Expand Down
53 changes: 53 additions & 0 deletions src/builtins/records.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,59 @@ pub fn call_record(xs: &[Calcit]) -> Result<Calcit, CalcitErr> {
}
}

/// takes a record and pairs of key value(flatterned), and update the record. raise error if key not existed in the record
pub fn record_with(xs: &[Calcit]) -> Result<Calcit, CalcitErr> {
let args_size = xs.len();
if args_size < 3 {
return CalcitErr::err_nodes("&record:with expected at least 3 arguments, got:", xs);
}
match &xs[0] {
Calcit::Record(
record @ CalcitRecord {
name,
fields: def_fields,
values: v0,
class,
},
) => {
if (args_size - 1).rem(2) == 0 {
let size = (args_size - 1) / 2;
let mut values: Vec<Calcit> = (**v0).to_owned();

for idx in 0..size {
let k_idx = idx * 2 + 1;
let v_idx = k_idx + 1;
match &xs[k_idx] {
Calcit::Tag(s) => match record.index_of(s.ref_str()) {
Some(pos) => {
xs[v_idx].clone_into(&mut values[pos]);
}
None => return CalcitErr::err_str(format!("unexpected field {s} for {def_fields:?}")),
},
Calcit::Symbol { sym: s, .. } | Calcit::Str(s) => match record.index_of(s) {
Some(pos) => {
xs[v_idx].clone_into(&mut values[pos]);
}
None => return CalcitErr::err_str(format!("unexpected field {s} for {def_fields:?}")),
},
a => return CalcitErr::err_str(format!("expected field in string/tag, got: {a}")),
}
}

Ok(Calcit::Record(CalcitRecord {
name: name.to_owned(),
fields: def_fields.to_owned(),
values: Arc::new(values),
class: class.to_owned(),
}))
} else {
CalcitErr::err_nodes("&record:with expected pairs, got:", xs)
}
}
a => CalcitErr::err_str(format!("&record:with expected a record as prototype, got: {a}")),
}
}

pub fn get_class(xs: &[Calcit]) -> Result<Calcit, CalcitErr> {
let args_size = xs.len();
if args_size != 1 {
Expand Down
2 changes: 2 additions & 0 deletions src/calcit/proc_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ pub enum CalcitProc {
NewClassRecord,
#[strum(serialize = "&%{}")]
NativeRecord,
#[strum(serialize = "&record:with")]
NativeRecordWith,
#[strum(serialize = "&record:class")]
NativeRecordClass,
#[strum(serialize = "&record:with-class")]
Expand Down
12 changes: 12 additions & 0 deletions src/cirru/calcit-core.cirru
Original file line number Diff line number Diff line change
Expand Up @@ -1843,6 +1843,18 @@
|' $ %{} :CodeEntry (:doc "|alias for []")
:code $ quote
def "'" []
|record-with $ %{} :CodeEntry (:doc "|macro to extend existing record with new values in pairs, internally using &record:with which takes flattern items")
:code $ quote
defmacro record-with (record & pairs)
; "check if args are in pairs"
if
not $ and (list? pairs)
every? pairs $ fn (xs)
and (list? xs) (&= 2 $ count xs)
raise $ str-spaced "|record-with expects a list of pairs (each with exactly two elements), got:" pairs
; "call &record:with"
quasiquote $ &record:with ~record $ ~@ $ &list:concat & pairs

:ns $ %{} :CodeEntry (:doc "|built-in function and macros in `calcit.core`")
:code $ quote
ns calcit.core $ :require
22 changes: 22 additions & 0 deletions ts-src/js-record.mts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ export let _$n__PCT__$M_ = (proto: CalcitValue, ...xs: Array<CalcitValue>): Calc
}
};

/// update record with new values
export let _$n_record_$o_with = (proto: CalcitValue, ...xs: Array<CalcitValue>): CalcitValue => {
if (proto instanceof CalcitRecord) {
if (xs.length % 2 !== 0) {
throw new Error("Expected even number of key/value");
}
let values = proto.values.slice();
for (let i = 0; i < xs.length; i += 2) {
let k = castTag(xs[i]);
let v = xs[i + 1];
let idx = findInFields(proto.fields, k);
if (idx < 0) {
throw new Error(`Cannot find field ${k} among ${proto.fields}`);
}
values[idx] = v;
}
return new CalcitRecord(proto.name, proto.fields, values, proto.klass);
} else {
throw new Error("Expected prototype to be a record");
}
};

export let _$n_record_$o_get_name = (x: CalcitRecord): CalcitTag => {
if (x instanceof CalcitRecord) {
return x.name;
Expand Down

0 comments on commit 9b9f080

Please sign in to comment.