--- title:  "dev tools 🛠️🧰 (all models)" order:  500 author:   - name: "Declan Naughton 🧮👨💻🧉"     url: "https://calcwithdec.dev/about.html" description:  "calculang model developer tools" format:   html:     echo: false     resources:       - '../models/main/*.js'       - '../models/main/*.js.map'       - '../models/main/*_esm/*.cul.js'       - '../models/main/*_esm/*.mjs'       - '../models/bounce/*' # not required by any other page       - '../models/bounce/*_esm/*' # not required by any other page       - '../models/coffee/*' # not required by any other page       - '../models/coffee/*_esm/*' # not required by any other page       - '../models/**'       - '../models/climate-simple/climate-simple_esm/**' --- ## formula-inputs matrix ```{ojs} md`${function_inputs_table(introspection)}` // this should be better than a matrix: it should be an indented tree where it's possible to follow the logic of the compiler incl. where inputs get summarised // useful to do nomemo/memo option? (should always be the same, be aware memo functionality will change) ``` ## cul scope id graph ```{ojs} viewof show_query_string = Inputs.checkbox(["show query string?"]) viewof scope_id_graph_nomemo = Inputs.checkbox(["nomemo"], {value: ["nomemo"]}) // defaults here are setup to protect you from what really happens when memo is on ! dot`${scope_id_graph}` ``` ## graph ```{ojs} viewof graph_nomemo = Inputs.checkbox(["nomemo"], {value: ["nomemo"]}) g = dot`${graph_functions(graph_nomemo.length ? introspection_nomemo : introspection)}` DOM.download(() => serializeSVG(g), undefined, "Download SVG") ``` ## calculang formulae & generated js ```{ojs} viewof calculang_source_nomemo = Inputs.checkbox(["nomemo"], {value: ["nomemo"]}) calculang_source_introspection = calculang_source_nomemo.length ? introspection_nomemo : introspection viewof cul_scope_id = Inputs.radio(_.range(0,Object.keys(calculang_source_introspection.cul_scope_ids_to_resource).length), {label: "cul_scope_id", value: 0 /*maybe nice to default to last one instead?*/}) ``` ## calculang 📝💬 ```{ojs} formulae_objs = Object.values(calculang_source_introspection.cul_functions).filter(d => d.reason == 'definition' && inputs.indexOf(d.name+'_in') == -1) input_formulae_objs = Object.values(calculang_source_introspection.cul_functions).filter(d => d.reason == 'definition' && inputs.indexOf(d.name+'_in') != -1) inputs = Object.values(calculang_source_introspection.cul_functions).filter(d => d.reason == 'input definition').map(d => d.name).sort() cul_fetch = await fetch(`../../${entrypoint_no_cul_js}${calculang_source_nomemo.length ? '-nomemo_esm' : '_esm'}/cul_scope_${cul_scope_id}.cul.js`) cul = cul_fetch.text() md` **Inputs** are \`${inputs.join('\`, \`')}\` **Formulae**: ~~~js ${ formulae_objs.map(f => cul.split('\n').filter((d,i) => i >= f.loc.start.line-1 && i < f.loc.end.line).join('\n').slice(13)).join('\n\n') } ~~~ ` ``` ```{ojs} md` ~~~js ${ input_formulae_objs.map(f => cul.split('\n').filter((d,i) => i >= f.loc.start.line-1 && i < f.loc.end.line).join('\n').slice(13)).join('\n\n') } ~~~ ` ``` ## ✨ js ```{ojs} esm_fetch = await fetch(`../../${entrypoint_no_cul_js}${calculang_source_nomemo.length ? '-nomemo_esm' : '_esm'}/cul_scope_${cul_scope_id}.mjs`) esm = esm_fetch.text() md` **Inputs** are \`${inputs.join('\`, \`')}\` **Formulae**: ~~~js ${ formulae_objs.map(f => esm.split('\n').filter((d,i) => i >= f.loc.start.line-1 && i < f.loc.end.line).join('\n').slice(13)).join('\n\n') } ~~~ ` ``` ```{ojs} md` ~~~js ${ input_formulae_objs.map(f => esm.split('\n').filter((d,i) => i >= f.loc.start.line-1 && i < f.loc.end.line).join('\n').slice(13)).join('\n\n') } ~~~ ` ``` ## Appendix ```{ojs} import {calcuvizspec, graph_functions, function_inputs_table} from "@declann/little-calcu-helpers" entrypoints_all = [ // see list-cul.sh/txt 'models/donut/donut.cul.js', //'models/donut/donut-nomemo.cul.js //'models/main/main-nomemo.cul.js 'models/main/main.cul.js', 'models/adders/n-bit-adder.cul.js', 'models/adders/half-adder.cul.js', 'models/adders/full-adder.cul.js', 'models/adders/gates.cul.js', 'models/kaya/kaya.cul.js', //'models/kaya/kaya-nomemo.cul.js 'models/projectile/projectile.cul.js', //'models/projectile/projectile-nomemo.cul.js', 'models/zapis/zapis.cul.js', //'models/zapis/zapis-nomemo.cul.js 'models/coffee/coffee-demand-curve.cul.js', //'models/coffee/coffee-demand-curve-nomemo.cul.js 'models/coffee/coffee.cul.js', //'models/loan-validator/simple-loan-nomemo.cul.js 'models/loan-validator/simple-loan.cul.js', 'models/bounce/bounce-0.cul.js', 'models/bounce/bounce-1.cul.js', 'models/bounce/bounce-2.cul.js', 'models/bounce/bounce-3.cul.js', 'models/bounce/bounce-4.cul.js', 'models/bounce/bounce-5.cul.js', 'models/bounce/bounce-6.cul.js', 'models/raycasting/raycasting.cul.js', 'models/climate-simple/climate-simple.cul.js', 'models/climate-simple/climate-simple_esm/ramp.cul.js', 'models/climate-simple/ClimateMA.cul.js', 'models/heart/heart-contour.cul.js', 'models/vek/vek.cul.js' ] viewof entrypoint = Inputs.select(entrypoints_all, {label:'entrypoint'}) entrypoint_no_cul_js = entrypoint.slice(0,-7) introspection_fetch = await fetch(`../../${entrypoint_no_cul_js}.introspection.json`) introspection = introspection_fetch.json({typed:true}) introspection_nomemo_fetch = await fetch(`../../${entrypoint_no_cul_js}-nomemo.introspection.json`) introspection_nomemo = introspection_nomemo_fetch.json({typed:true}) ``` ```{ojs} // reference: https://observablehq.com/@mbostock/saving-svg serializeSVG = {   const xmlns = "http://www.w3.org/2000/xmlns/";   const xlinkns = "http://www.w3.org/1999/xlink";   const svgns = "http://www.w3.org/2000/svg";   return function serialize(svg) {     svg = svg.cloneNode(true);     const fragment = window.location.href + "#";     const walker = document.createTreeWalker(svg, NodeFilter.SHOW_ELEMENT);     while (walker.nextNode()) {       for (const attr of walker.currentNode.attributes) {         if (attr.value.includes(fragment)) {           attr.value = attr.value.replace(fragment, "#");         }       }     }     svg.setAttributeNS(xmlns, "xmlns", svgns);     svg.setAttributeNS(xmlns, "xmlns:xlink", xlinkns);     const serializer = new window.XMLSerializer;     const string = serializer.serializeToString(svg);     return new Blob([string], {type: "image/svg+xml"});   }; } ``` ```{ojs} scope_id_graph_introspection = scope_id_graph_nomemo.length ? introspection_nomemo : introspection; scope_id_graph_links = Object.entries(scope_id_graph_introspection.cul_scope_ids_to_resource).filter(([cul_scope_id]) => cul_scope_id != 0).map(([cul_scope_id, resource]) => new URLSearchParams(resource.split('?').pop()).get('cul_scope_id') + ' -> ' + new URLSearchParams(resource).get('cul_parent_scope_id')) scope_id_graph_nodes = Object.entries(scope_id_graph_introspection.cul_scope_ids_to_resource).map(d => (`${d[0]} [${ d[0] == 0 ? 'color="green" style="filled" ' :  'color="yellow" style="filled" '}label="[${d[0]}]: ${show_query_string.length ? d[1] : d[1].split('?')[0]}"]`)) scope_id_graph = `digraph { rankdir="RL" node [shape="box"]; ${scope_id_graph_nodes.join('\n')} ${scope_id_graph_links.join('\n')} }` ```