@naanlang/naan 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE.md +3 -4
  2. package/README.md +20 -2
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +86 -17
  5. package/dist/naan.min.js +7 -7
  6. package/frameworks/browser/browser.nlg +27 -3
  7. package/frameworks/browser/sworker.js +17 -17
  8. package/frameworks/browser/terminals.nlg +1 -1
  9. package/frameworks/browser/workers.nlg +8 -1
  10. package/frameworks/client/psm_client.nlg +2 -1
  11. package/frameworks/common/common.nlg +34 -6
  12. package/frameworks/node/filesystem.nlg +40 -33
  13. package/frameworks/node/gitter.nlg +9 -4
  14. package/frameworks/node/https_request.nlg +19 -14
  15. package/frameworks/node/psm_server.nlg +3 -3
  16. package/frameworks/project/build.nlg +182 -73
  17. package/frameworks/project/proj_console.nlg +1 -1
  18. package/frameworks/project/proj_lambda.nlg +1 -1
  19. package/frameworks/project/projects.nlg +5 -5
  20. package/frameworks/running/debugnub.nlg +1 -1
  21. package/frameworks/running/executors.nlg +1 -1
  22. package/frameworks/running/running.nlg +32 -10
  23. package/frameworks/running/sourcecode.nlg +2 -2
  24. package/frameworks/storage/csv.nlg +2 -2
  25. package/frameworks/storage/dbt_pouch.nlg +1 -1
  26. package/frameworks/storage/psm.nlg +21 -5
  27. package/frameworks/storage/psm_dbtables.nlg +4 -4
  28. package/frameworks/storage/resources.nlg +3 -2
  29. package/lib/browser/env_web.js +90 -21
  30. package/lib/core/naanlib.js +7 -7
  31. package/package.json +1 -1
  32. package/plugins/serviceAws/aws/aws-sdk-node.min.js +1 -1
  33. package/plugins/serviceAws/aws/aws-sdk.min.js +1 -1
  34. package/plugins/serviceAws/aws/lambda_ws_init.nlg +2 -1
  35. package/plugins/serviceAws/aws_dynamo.nlg +36 -29
  36. package/plugins/serviceAws/aws_dynextra.nlg +110 -5
  37. package/plugins/serviceAws/aws_lambda.nlg +4 -8
  38. package/plugins/serviceAws/aws_sqs.nlg +113 -0
  39. package/plugins/serviceAws/dbt_aws.nlg +2 -2
  40. package/plugins/serviceAws/serviceAws.nlg +1 -1
  41. package/plugins/serviceYC/generic_api.yaml +36 -0
  42. package/plugins/serviceYC/naanide-relay-index.js +148 -0
  43. package/plugins/serviceYC/serviceYC.nlg +59 -0
  44. package/plugins/serviceYC/yc_function.nlg +161 -0
  45. package/test/harness.nlg +2 -1
  46. package/test/test_01_core.nlg +2 -2
  47. package/test/test_02_context.nlg +2 -2
  48. package/test/test_05_strings.nlg +13 -1
  49. package/test/test_06_lingoparse.nlg +49 -14
  50. package/test/test_07_lingo.nlg +9 -9
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2022 by Richard C. Zulch
9
+ * Copyright (c) 2019-2023 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -47,6 +47,20 @@
47
47
  * srcpath - the source path in common to all sources
48
48
  * destpath - the destination path in common to all outputs
49
49
  *
50
+ * Special paths:
51
+ * srcpath - the Build object looks for all sources within the srcpath specified when it's
52
+ * instantiated. For example, the files in a group are located in the join of the
53
+ * paths: <srcpath> / <group.input> / <srcname.xx>.
54
+ * destpath - the Build object writes all output files within the destpath specified when
55
+ * it's instantiated. For example, the files output from a group are located in
56
+ * the join of the paths: <destpath> / <group.output> / <srcname.xx>. Note that
57
+ * group.input is used to locate the files, but is not part of the output.
58
+ * @ - the @ character stands in for a persistent, temporary directory within the
59
+ * build folder that replaces srcpath or destpath. It can be used in fields that
60
+ * are otherwise absolute: group.input, group.output, and template paths. Build
61
+ * detects piplines formed from temporary files and processes them in order from
62
+ * beginning to end.
63
+ *
50
64
  * Templates:
51
65
  * A dictionary where each key is a destfile as described above, and the data for the key
52
66
  * is the path to a template file relative to srcpath. When a template file exists for multiple-
@@ -76,6 +90,7 @@
76
90
  * licfile: <license file, path relative to srcpath>
77
91
  * verfile: <version file, path relative to srcpath>
78
92
  * buildno: <build number file, path relative to srcpath>
93
+ * xvrfile: <external version file, path relative to srcpath>
79
94
  * run: {
80
95
  * "open": "Nide_LaunchMac.tool",
81
96
  * "args": []
@@ -129,6 +144,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
129
144
  build = new(object, this)
130
145
  build.srcpath = srcpath
131
146
  build.destpath = destpath
147
+ build.intpath = JSpath.join(destpath, "tmp")
132
148
  build.tasks = { // all tasks counter
133
149
  copy: 0,
134
150
  version: 0,
@@ -152,11 +168,16 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
152
168
  build.verfilepath = JSpath.join(build.srcpath, rules.verfile)
153
169
  if rules.buildno
154
170
  build.buildnopath = JSpath.join(build.srcpath, rules.buildno)
171
+ if rules.xvrfile
172
+ build.xvrfilepath = JSpath.join(build.srcpath, rules.xvrfile)
155
173
  if rules.templates { // templates for catenates
156
174
  let (tpath, writepath) {
157
175
  build.tepaths = { } // template for each catenate output
158
176
  for tpath in rules.templates {
159
- writepath = JSpath.join(build.destpath, tpath)
177
+ if tpath.startsWith("@")
178
+ writepath = JSpath.join(build.intpath, tpath.substring(1))
179
+ else
180
+ writepath = JSpath.join(build.destpath, tpath)
160
181
  build.tepaths[writepath] = JSpath.join(build.srcpath, rules.templates[tpath])
161
182
  }
162
183
  build.testats = { }
@@ -370,7 +391,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
370
391
  // Run the zip operation on a worker.
371
392
  //
372
393
 
373
- closure zip(files, options, local result, annote) {
394
+ closure zip(files, options, local result) {
374
395
  if !build.zipper
375
396
  build.zipper = ExecutorParallel({ // create worker for zipping
376
397
  maxcount: 1 // stupid Zip lib can't do more than one
@@ -506,7 +527,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
506
527
  // Run the parseAndPack operation on a worker.
507
528
  //
508
529
 
509
- closure parseAndPack(text, fpath, options, local result, annote) {
530
+ closure parseAndPack(text, fpath, options, local result) {
510
531
  if !build.parallel
511
532
  build.parallel = ExecutorParallel({ // create worker pool
512
533
  basename: "build"
@@ -616,7 +637,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
616
637
  }
617
638
 
618
639
  groups = new(build.groups) // copy so we can append more groups as needed
619
- sourcemax = groups.reduce(function(acc, group) { Math.max(acc, group.sources.length) }, 0)
640
+ sourcemax = 10000
620
641
  catenates = { } // catenate groups
621
642
  pending = new(nonce)
622
643
  pending.active = 0
@@ -631,15 +652,22 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
631
652
  // added as a separate new group. A counter is kept of all outstanding async functions, and
632
653
  // analyze does not return until all pending async operations have completed.
633
654
  //
634
- closure processOneGroup(group, local error, indirpath, outdirpath, destcheck,
655
+ closure processOneGroup(group, local indirpath, outdirpath, destcheck,
635
656
  groupVerStat, source, sdex) {
636
657
  ++pending.active
637
658
  future(function test1() {
638
- if group.input
659
+ if group.input.startsWith("@") { // intermediate directory
660
+ group.intSource = true
661
+ indirpath = JSpath.join(build.intpath, group.input.substring(1))
662
+ }
663
+ else if group.input
639
664
  indirpath = JSpath.join(build.srcpath, group.input)
640
665
  else
641
666
  indirpath = build.srcpath
642
- outdirpath = JSpath.join(build.destpath, group.output)
667
+ if group.output.startsWith("@")
668
+ outdirpath = JSpath.join(build.intpath, group.output.substring(1))
669
+ else
670
+ outdirpath = JSpath.join(build.destpath, group.output)
643
671
  destcheck = new(group.destcheck).reverse() // get next destination to check
644
672
  groupVerStat = verfileStat
645
673
  if group.depend_buildno {
@@ -655,11 +683,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
655
683
  else
656
684
  outfile = JSpath.join(outdirpath, source)
657
685
  fs.info(infile, false, function(error, instat, local listing, addgroup, file) {
658
- if error
659
- build.errorlist.push(error)
660
- else if instat.type != "directory"
661
- addEntry(infile, outfile, instat, sdex) // add execution entry if needed
662
- else { // add a new group for directory source
686
+ if !error && instat.type == "directory" {
663
687
  `(error, listing) = fs.dirList(infile, { stat: true })
664
688
  if error
665
689
  build.errorlist.push(error)
@@ -681,6 +705,10 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
681
705
  groups.push(addgroup)
682
706
  }
683
707
  }
708
+ else if !error || group.intSource
709
+ addEntry(infile, outfile, instat, sdex) // add execution entry
710
+ else
711
+ build.errorlist.push(error)
684
712
  --pending.active
685
713
  doMore()
686
714
  })
@@ -691,12 +719,18 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
691
719
 
692
720
  // Add an entry to execution list if it's needed.
693
721
  //
694
- closure addEntry(infile, outfile, instat, sdex, local error, outstat, entry) {
722
+ closure addEntry(infile, outfile, instat, sdex, local entry) {
723
+ if sdex >= sourcemax {
724
+ build.errorlist.push(Error("Too many sources in one group:", groupno.group))
725
+ return
726
+ }
695
727
  entry = {
696
728
  readpath: infile,
697
729
  writepath: outfile
698
730
  }
699
- if group.input // insource is the name within zip/cat
731
+ if group.input.startsWith("@")
732
+ entry.insource = JSpath.join(group.input.substring(1), group.sources[sdex])
733
+ else if group.input // insource is the name within zip/cat
700
734
  entry.insource = JSpath.join(group.input, group.sources[sdex])
701
735
  else
702
736
  entry.insource = group.sources[sdex]
@@ -771,23 +805,24 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
771
805
  ++pending.active
772
806
  if destcheck { // destcheck is special target for determining if task needed
773
807
  entry.destcheck = JSpath.join(outdirpath, destcheck.pop())
774
- fs.info(entry.destcheck, false, addIfOutOfDate)
808
+ fs.info(entry.destcheck, false, addOne)
775
809
  } else
776
- fs.info(outfile, false, addIfOutOfDate)
810
+ fs.info(outfile, false, addOne)
777
811
 
778
- function addIfOutOfDate(error, outstat, local entryop) {
812
+ function addOne(error, outstat, local entryop) {
779
813
  if error || toint(instat.mtimeMs) > toint(outstat.mtimeMs) {
814
+ entry.outOfDate = true
780
815
  catenates[entry.writepath] = true // this entire group needed
781
- build.filelist.push(entry) // destination is out of date
816
+ build.filelist.push(entry)
782
817
  entryop = "make:"
783
818
  } else if entry.catorder {
784
819
  build.filelist.push(entry) // needed if any in group out of date
785
- entryop = "maybe-needed:"
820
+ entryop = "cat-check:"
786
821
  }
787
822
  else
788
823
  entryop = "up-to-date:"
789
824
  if debugMake
790
- debuglog(entryop, entry.*, instat.mtimeMs, outstat.mtimeMs, entry.readpath, entry.writepath)
825
+ debuglog(entryop, entry.readpath, entry.*, instat.mtimeMs, outstat.mtimeMs, entry.writepath)
791
826
  --pending.active
792
827
  doMore()
793
828
  }
@@ -808,6 +843,32 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
808
843
  doMore()
809
844
  pending.wait()
810
845
 
846
+ //
847
+ // Cross-check inputs from intermediate files that will be built
848
+ //
849
+
850
+ let (outint, entry) {
851
+ outint = {}
852
+ for entry in build.filelist
853
+ if entry.outOfDate && entry.writepath.startsWith(build.intpath) {
854
+ outint[entry.writepath] = entry // at least one component out of date
855
+ break
856
+ }
857
+ for entry in build.filelist
858
+ if outint[entry.writepath].catorder > entry.catorder
859
+ outint[entry.writepath] = entry // find first component
860
+ for entry in build.filelist
861
+ if outint[entry.readpath] {
862
+ entry.outOfDate = true
863
+ catenates[entry.writepath] = true // this entire group needed
864
+ entry.depends = outint[entry.readpath]
865
+ if !entry.depends.done
866
+ entry.depends.done = new(nonce)
867
+ if debugMake
868
+ debuglog("add-make:", entry.readpath, entry.*, entry.writepath)
869
+ }
870
+ } ()
871
+
811
872
  //
812
873
  // Remove unneeded catenate entries and index the remainder
813
874
  //
@@ -820,11 +881,16 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
820
881
  else if catenates[entry.writepath] {
821
882
  if !renumber[entry.writepath]
822
883
  renumber[entry.writepath] = []
823
- renumber[entry.writepath].push(entry) // this catenate is needed
884
+ if renumber[entry.writepath].indexOf(entry) < 0
885
+ renumber[entry.writepath].push(entry) // this catenate is needed
886
+ entry.outOfDate = true // this entry is needed
824
887
  true
825
888
  } else
826
889
  false // this one is not
827
890
  })
891
+ build.filelist = build.filelist.filter(function(entry) {
892
+ entry.outOfDate
893
+ })
828
894
  for wpath in renumber { // assign entries in catorder
829
895
  renumber[wpath].sort(function(a,b) { a.catorder <=> b.catorder })
830
896
  catindex = 0
@@ -845,8 +911,8 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
845
911
  // checked and any that have all the parts are written out. Catenates that don't have all their
846
912
  // parts are reported as an error in addition to the error that caused them to be incomplete.
847
913
  //
848
- closure execute(local error, licFileDefs, buildno, verFileDefs, version, entries, pending,
849
- baddestpaths, catenates) {
914
+ closure execute(local error, licFileDefs, buildno, verFileDefs, xvrFileDefs,
915
+ entries, pending, baddestpaths, catenates) {
850
916
  if build.tasks.license > 0
851
917
  `(error, licFileDefs) = readSubstitutionFile(build.licfilepath)
852
918
  if build.tasks.version > 0 {
@@ -857,9 +923,15 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
857
923
  } else
858
924
  build.buildno = buildno = toint(buildno.trim())
859
925
  build.buildno = buildno
860
- `(error, verFileDefs) = readSubstitutionFile(build.verfilepath, {
861
- BuildNumber: buildno
862
- })
926
+ if build.xvrfilepath {
927
+ `(error, xvrFileDefs) = readSubstitutionFile(build.xvrfilepath)
928
+ if error
929
+ build.errorlist.push(error)
930
+ }
931
+ if !xvrFileDefs
932
+ xvrFileDefs = { }
933
+ xvrFileDefs.BuildNumber = buildno
934
+ `(error, verFileDefs) = readSubstitutionFile(build.verfilepath, xvrFileDefs)
863
935
  build.version = verFileDefs["Version"]
864
936
  verFileDefs["CacheBuster"] = HashMD5(build.version.concat(Math.random()))
865
937
  }
@@ -889,6 +961,8 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
889
961
  closure processOneFile(entry, local result, content, error) {
890
962
  ++pending.active
891
963
  future(function build1(local dirpath) {
964
+ if entry.depends
965
+ entry.depends.done.wait() // wait for our precursor
892
966
  dirpath = JSpath.dirname(entry.writepath)
893
967
  if baddestpaths[dirpath]
894
968
  { } // error already reported
@@ -904,7 +978,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
904
978
  erase: true // not really needed
905
979
  overwrite: true // overwrite files
906
980
  force: true // ignore modify date
907
- preserve: true // preserve attributes
981
+ idmode: true // preserve ids and mode
908
982
  })
909
983
  if error // deepcopy returns a list of errors
910
984
  error = error.0 // but we're only doing one item
@@ -936,10 +1010,20 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
936
1010
  if !catenates[entry.writepath]
937
1011
  catenates[entry.writepath] = new(Array(entry.catcount))
938
1012
  entry.content = content
939
- catenates[entry.writepath][entry.catindex] = entry }
940
- else
1013
+ catenates[entry.writepath][entry.catindex] = entry
1014
+ if catReady(entry.writepath) {
1015
+ `(error, result) = writeCatenate(entry.writepath)
1016
+ if error
1017
+ build.errorlist.push(error)
1018
+ catenates[entry.writepath].0.done.signal(error)
1019
+ }
1020
+ }
1021
+ else {
941
1022
  `(error, result) = fs.writeFile(entry.writepath, content)
942
- }
1023
+ entry.done.signal(error)
1024
+ }
1025
+ } else
1026
+ entry.done.signal(error)
943
1027
  }
944
1028
  if error
945
1029
  build.errorlist.push(error)
@@ -960,52 +1044,76 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
960
1044
  if pending.remaining > 0
961
1045
  pending.wait() // don't wait if nothing to do
962
1046
 
1047
+ // catReady
963
1048
  //
964
- // Write out any completed catenates
1049
+ // Return true iff the catenate has all its elements.
965
1050
  //
1051
+ function catReady(writepath, local entry) {
1052
+ for entry in catenates[writepath]
1053
+ if !entry.content
1054
+ return (false)
1055
+ true
1056
+ }
966
1057
 
967
- let (writepath, template, entry, content, catsub, dozip, docat, fsoptions, zipmap, error, result) {
968
- for :out1 writepath in catenates {
969
- template = build.templates[writepath] // optional in some cases ### should warn in others
970
- catsub = { }
971
- for entry in catenates[writepath] { // error check and substitution build
972
- if !entry.content {
973
- build.errorlist.push(Error("Catenate incomplete:", writepath))
974
- continue :out1 }
975
- catsub[entry.insource] = entry.content
976
- if entry.zip
977
- dozip = true
978
- if entry.catenate
979
- docat = true
1058
+ // writeCatenate
1059
+ //
1060
+ // Write out a catenate, returning a standard result tuple.
1061
+ //
1062
+ function writeCatenate(writepath,
1063
+ local template, entry, content, catsub, dozip, docat, fsoptions, zipmap, error, result) {
1064
+ template = build.templates[writepath] // optional in some cases ### should warn in others
1065
+ catsub = { }
1066
+ for entry in catenates[writepath] { // error check and substitution build
1067
+ if !entry.content
1068
+ return (list(Error("Incomplete zip/cat:", writepath)))
1069
+ catsub[entry.insource] = entry.content
1070
+ if entry.zip
1071
+ dozip = true
1072
+ if entry.catenate
1073
+ docat = true
1074
+ }
1075
+ if docat { // catenate the files
1076
+ if dozip
1077
+ return (list(Error("Cannot combine zip/cat into:", writepath)))
1078
+ if template {
1079
+ `(content, result) = applySubstitutions(template, catsub)
1080
+ if length(catsub) != result
1081
+ return (list(Error("Matched ", result, " of ", length(catsub),
1082
+ "substitutions in template:", build.tepaths[writepath])))
980
1083
  }
981
- if docat { // catenate the files
982
- if template {
983
- `(content, result) = applySubstitutions(template, catsub)
984
- if length(catsub) != result
985
- build.errorlist.push(Error("Matched ", result, " of ", length(catsub),
986
- "substitutions in template:", build.tepaths[writepath]))
987
- }
988
- else
989
- content = catenates[writepath].map(function(entry){entry.content}).join("\n")
990
- if dozip {
991
- zipmap = { }
992
- zipmap[JSpath.basename(writepath)] = content
993
- `(error, content) = zip(zipmap) // compress the catenated file
994
- }
995
- } else {
996
- if template
997
- zipmap = applyZipTemplate(template, catsub)
998
- else
999
- zipmap = trimZipPaths(catsub)
1000
- `(error, content) = zip(zipmap) // compress each of the files
1084
+ else
1085
+ content = catenates[writepath].map(function(entry){entry.content}).join("\n")
1086
+ if dozip {
1087
+ zipmap = { }
1088
+ zipmap[JSpath.basename(writepath)] = content
1089
+ `(error, content) = zip(zipmap) // compress the catenated file
1001
1090
  }
1002
- if dozip
1003
- fsoptions = { encoding: "base64" }
1004
- if !error
1005
- `(error, result) = fs.writeFile(writepath, content, fsoptions)
1006
- if error
1007
- build.errorlist.push(error)
1091
+ } else {
1092
+ if template
1093
+ zipmap = applyZipTemplate(template, catsub)
1094
+ else
1095
+ zipmap = trimZipPaths(catsub)
1096
+ `(error, content) = zip(zipmap) // compress each of the files
1008
1097
  }
1098
+ if dozip
1099
+ fsoptions = { encoding: "base64" }
1100
+ if !error
1101
+ `(error, result) = fs.writeFile(writepath, content, fsoptions)
1102
+ if error
1103
+ list(error)
1104
+ else
1105
+ list(false, { ok: true })
1106
+ }
1107
+
1108
+ //
1109
+ // Check for any incomplete catenates.
1110
+ //
1111
+
1112
+ let (writepath, entry) {
1113
+ for writepath in catenates
1114
+ for entry in catenates[writepath]
1115
+ if !entry.content
1116
+ build.errorlist.push(Error("Missing zip/cat element", writepath, "--", entry.readpath))
1009
1117
  } ()
1010
1118
  }
1011
1119
 
@@ -1086,7 +1194,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
1086
1194
  `(error, executor) = track.spawn(build.runway.spawn.0, build.runway.spawn.1, "Build-".concat(build.buildnum), {
1087
1195
  startup: {
1088
1196
  initcmds: maintext
1089
- dirpath: JSpath.join(fs.rootpath, build.destpath)
1197
+ dirpath: fs.path.resolve(fs.path.sep, fs.rootpath, build.destpath)
1090
1198
  }
1091
1199
  })
1092
1200
  if error
@@ -1136,6 +1244,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
1136
1244
  `(error, site) = track.spawn("V-Site", build.runway."v-site", fs, vpath)
1137
1245
  if error
1138
1246
  return (list(Error("Builder: cannot create virtual site", error)))
1247
+ sleep(1) // needed on Windows or it won't open, fiik why
1139
1248
  window = js.w.open(url, title, features)
1140
1249
  list(false, { ok: true }) // we don't know the executor
1141
1250
  }
@@ -1162,7 +1271,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
1162
1271
  //
1163
1272
  // Refresh after updating the v-site, returning true iff an existing vsite was found.
1164
1273
  //
1165
- build.refresh = function refresh(track local main, url_origin, found) {
1274
+ build.refresh = function refresh(track local url_origin, found) {
1166
1275
  if build.runway."v-site" {
1167
1276
  url_origin = js.w.location.origin.concat("/run", build.runway."v-site")
1168
1277
  for target in track.enumlist() {
@@ -46,7 +46,7 @@ closure proj_consoleInstance(projman, where, projID, local project) {
46
46
  // load
47
47
  //
48
48
  // Load the project, which fails if it does not already exist.
49
- project.load = closure load(local error, info) {
49
+ project.load = closure load(local result) {
50
50
  if project.loaded
51
51
  return (list(false, { ok: true }))
52
52
  result = project.config.load()
@@ -46,7 +46,7 @@ closure proj_lambdaInstance(projman, where, projID, local project) {
46
46
  // load
47
47
  //
48
48
  // Load the project, which fails if it does not already exist.
49
- project.load = closure load(local error, info) {
49
+ project.load = closure load(local result) {
50
50
  if project.loaded
51
51
  return (list(false, { ok: true }))
52
52
  result = project.config.load()
@@ -296,7 +296,7 @@ closure projConnector(projman, projtype, local connector, watch) {
296
296
  *
297
297
  * Nide.proj/
298
298
  * nide_cliser.cfg - JSON definition of the project (see below)
299
- * NaanIDE_version.txt - [default] substitution file defining 1.0.7+1 etc.
299
+ * NaanIDE_version.txt - [default] substitution file defining 1.0.9+1 etc.
300
300
  * NaanIDE_version_builds.txt - [default] contains current build number as decimal string
301
301
  * build/ - [optional] default build output location
302
302
  * NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
@@ -432,7 +432,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
432
432
  //
433
433
  // ### we need to update this for optional multiple-configuraiton builds
434
434
  //
435
- procon.create = closure create(name, local error, data, configpath) {
435
+ procon.create = closure create(name, local error, data) {
436
436
  if procon.loaded
437
437
  return (list(Error("Project.create: project already loaded")))
438
438
  if validate(name)
@@ -476,7 +476,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
476
476
  // Load the project, i.e. read the project configuration if valid. This is non-destructive.
477
477
  // The result is a standard error tuple with a dictionary result on success.
478
478
  //
479
- procon.load = closure load(local error, data, info, configpath) {
479
+ procon.load = closure load(local error, data, info) {
480
480
  if procon.loaded
481
481
  return (list(false, procon.configDict))
482
482
  if validate()
@@ -599,7 +599,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
599
599
  // Run the project by building and then launching.
600
600
  //
601
601
  procon.run = closure run(stage, buildonly,
602
- local error, rules, builder, buildpath, error, buildnum) {
602
+ local error, rules, builder, buildpath, buildnum) {
603
603
  `(error, rules) = buildrules(stage)
604
604
  if error
605
605
  return (list(error))
@@ -647,7 +647,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
647
647
  // }
648
648
  //
649
649
  procon.publish = closure publish(stage,
650
- local error, rules, where, publish, error, outOptions, outfs, inpath) {
650
+ local error, rules, where, publish, outOptions, outfs, inpath) {
651
651
  `(error, rules) = buildrules(stage)
652
652
  if error
653
653
  return (list(error))
@@ -390,7 +390,7 @@ closure DebugNub(local nub, debug, paused) {
390
390
  //
391
391
  // Report all our breakpoints as an array of dictionary items.
392
392
 
393
- nub.breakList = function breakList(local output, id, bp) {
393
+ nub.breakList = function breakList(local output, bpid) {
394
394
  output = []
395
395
  for bpid in debug.breakpoints
396
396
  output.push(breakInfo(bpid))
@@ -538,7 +538,7 @@ closure execLambda(track, url, name, workerID, local target, nlregex, clientID,
538
538
  // onmessage
539
539
  // A message was received from the worker via WebSockets.
540
540
 
541
- target.ws.onmessage = function onmessage(e, local message, msg) {
541
+ target.ws.onmessage = function onmessage(e) {
542
542
  if target.msgQueue
543
543
  target.msgQueue.push(e.data)
544
544
  else
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2021 by Richard C. Zulch
9
+ * Copyright (c) 2021-2023 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -48,17 +48,26 @@ function baseproc(procdef) {
48
48
  /*
49
49
  * pathmatch
50
50
  *
51
- * Given a parent symbol and a tuple of child procedure symbols within it, return the child
52
- * procedure named by the path in the tuple.
51
+ * Given a parent symbol and a tuple of child procedures within it, return the child procedure
52
+ * named by the path in the tuple. Each element in the child procedures is either a symbol == to the
53
+ * procedure or, in the case of lambdas, a tuple comprising the procedure symbol and an integer
54
+ * specifying the nth matching child. This is necessary because a procedure can have any number of
55
+ * children named lambda, and we need to pick the right ones even though we don't have identical
56
+ * symbols.
53
57
  *
54
58
  */
55
59
 
56
- function pathmatch(parent, path, local segment) {
60
+ function pathmatch(parent, path, local segment, nth, child) {
57
61
  if empty(path)
58
62
  return (parent)
59
- segment = tostring(pop(path))
63
+ segment = pop(path)
64
+ if tuple(segment) { // select nth instance of matching symbol
65
+ nth = segment.1
66
+ segment = segment.0
67
+ } else
68
+ nth = 0
60
69
  for child in parent@\.children
61
- if tostring(child) == segment
70
+ if child == segment && nth-- == 0
62
71
  return (pathmatch(child, path))
63
72
  false
64
73
  };
@@ -68,14 +77,27 @@ function pathmatch(parent, path, local segment) {
68
77
  * procpath
69
78
  *
70
79
  * Return a tuple of parent prcoedure symbols for the specified child procedure. Note that this
71
- * path includes the parent but pathmatch above requires the parent as a separate argument.
80
+ * path includes the parent but pathmatch above requires the parent as a separate argument. As noted
81
+ * above, the segments of the path are either a procedure symbol, or a tuple of symbol and integer.
72
82
  *
73
83
  */
74
84
 
75
- function procpath(procdef, local path) {
85
+ function procpath(procdef, local path, segment, parent, peers, nth, peer) {
76
86
  loop {
77
- push(procdef.1, path)
78
- parent = procdef.1@\.parent
87
+ segment = procdef.1
88
+ parent = segment@\.parent
89
+ if segment == lambda && parent { // lambdas all have same name
90
+ peers = parent@\.children
91
+ nth = 0
92
+ for peer in peers {
93
+ if peer === segment
94
+ break
95
+ if peer == segment
96
+ ++nth
97
+ }
98
+ push(list(segment, nth), path) // we are the nth of the matching ones
99
+ } else
100
+ push(segment, path)
79
101
  if !parent
80
102
  break
81
103
  procdef = parent.proc
@@ -79,7 +79,7 @@ closure CodeManager(options, local coman, cache)
79
79
  // standard `(error, expr) format. The optional locals are symbols to provide context.
80
80
  //
81
81
  coman.parseExprs = function parseExprs(texts, locals,
82
- local output, text, source, registry, dialect, error, expr)
82
+ local output, text, source, registry, dialect)
83
83
  {
84
84
  output = []
85
85
  for text in texts {
@@ -124,7 +124,7 @@ closure CodeManager(options, local coman, cache)
124
124
  //
125
125
  // Get a CodeComposite from our cache, or false.
126
126
 
127
- coman.cachGet = function cachGet(cocode, manifest, local entry, item, mandefs) {
127
+ coman.cachGet = function cachGet(cocode, manifest, local item, mandefs) {
128
128
  if cache.modname != cocode.modname || cache.compname != cocode.compname || cache.manifest != manifest
129
129
  return (false)
130
130
  for item in manifest
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2022 by Richard C. Zulch
9
+ * Copyright (c) 2022-2023 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -84,7 +84,7 @@ function csvPrint(rows, local output, quoter, row, line, delim, col) {
84
84
  *
85
85
  */
86
86
 
87
- closure csvQuoteGenerator(delimiter, local rxComma, rxQuote) {
87
+ closure csvQuoteGenerator(delimiter, local rxDelimiter, rxQuote) {
88
88
  if !delimiter
89
89
  delimiter = ','
90
90
  rxDelimiter = RegExp('['.concat('"\\', delimiter, ']'))