11fca779588ed45f0b1d499d4fc759c8610f669f
commit 11fca779588ed45f0b1d499d4fc759c8610f669f
Author: spesk <spesk@pm.me>
Date: Fri Mar 18 10:00:38 2022 -0400

Initial commit

diff --git a/example.modmark b/example.modmark
new file mode 100644
index 0000000..0f96ae6
--- /dev/null
+++ b/example.modmark
@@ -0,0 +1,32 @@
+#lang reader modmark_br.rkt
+# Patch Spec
+# Representing an approximation of
+# a 909 kick. Module specs are imported
+# but don't yet exist
+
+Title: Kick Drum
+
+# By default "./modules" is included in the module path
+ModuleDir "/home/swatson/Repos/modular-markup/"
+
+import Module::BlueLantern::AsteroidOperatorVCF as VCF
+import Module::MakeNoise::Maths
+import Module::ALM::TangleQuartet as VCA
+import Module::Generic::Output
+
+set VCF.Pluck = on
+set VCF.Freq.position = 7
+
+set Maths.Cycle[1] = on
+set Maths.Rise[1].position = 7
+set Maths.Fall[1].position = 1
+set Maths.LogExp.position = 5
+
+set VCA.Input1.position = 5
+
+# Output of Maths env into VCF freq modulation
+connect Maths.1f VCF.PluckMod
+# Output of VCF into VCA
+connect VCF.Output.Overdrive VCA.Input1
+# VCA into soundcard/output
+connect VCA.Input1 Output.1
diff --git a/example.module b/example.module
new file mode 100644
index 0000000..1bd1c8d
--- /dev/null
+++ b/example.module
@@ -0,0 +1,43 @@
+# Module Spec
+
+Manufacturer: MakeNoise
+Module: Maths
+Revision: null
+- Input: Slew1
+- Input: Slew2
+- Input: Trig1
+- Input: Trig2
+- Input: Attenuveter1
+-- Position: [7-12],[1-5]
+- Input: Attenuveter2
+-- Position: [7-12],[1-5]
+- Input: Attenuveter3
+-- Position: [7-12],[1-5]
+- Input: Attenuveter4
+-- Position: [7-12],[1-5]
+- Input: Rise1
+-- Position: [7-12],[1-5]
+- Input: Rise2
+-- Position: [7-12],[1-5]
+- Input: Both1
+- Input: Both2
+- Input: Fall1
+-- Position: [7-12],[1-5]
+- Input: Fall2
+-- Position: [7-12],[1-5]
+- Input: Cycle1
+- Input: Cycle2
+- Knob: LogExp
+-- Position: [7-12],[1-5]
+- Button: Cycle1
+- Button: Cycle2
+- Output: 1f
+- Output: 1
+- Output: 2
+- Output: 3
+- Output: 4
+- Output: 4f
+- Output: Or
+- Output: Sum
+- Output: Inv
+-- Position: [1-3]
diff --git a/ideas.org b/ideas.org
new file mode 100644
index 0000000..0453893
--- /dev/null
+++ b/ideas.org
@@ -0,0 +1,27 @@
+
+Ideas for markup language that can describe modular patches.
+
+Ideally, output is a conceptual flow chart of patches. Additionally,
+patch construction can be done in an interactive shell, or via a patch file
+that provides intuituve shorthand for patch markup.
+
+Use cases:
+- random patch generation
+- patch "type checking"
+- brevity in expressing/sharing LARGE patches
+ - the trade off being you have to learn the language
+
+Idea is to place burden of patch notation on module notation, as opposed
+to connection notation. It's inconvinent to specific the module, but this can be
+community sourced and then should be trivially easy to do patch notation. See
+below example.
+
+
+
+Syntax would look something like:
+
+[[file:~/Repos/modular-markup/example.modmark][Kick Drum Example]]
+
+#+BEGIN_SRC lisp
+
+#+END_SRC
diff --git a/pl_proto.pl b/pl_proto.pl
new file mode 100755
index 0000000..6d40c24
--- /dev/null
+++ b/pl_proto.pl
@@ -0,0 +1,201 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+my $SOURCE_FILE = $ARGV[0];
+
+sub read_to_var($) {
+ my $file_path = shift;
+ my $content;
+ open(my $fh, '<', $file_path) or die "cannot open file $file_path";
+ {
+ local $/;
+ $content = <$fh>;
+ }
+ close($fh);
+
+ return $content;
+}
+
+my $src_content = read_to_var($SOURCE_FILE);
+
+sub trim($) {
+ my $str = shift;
+ $str =~ s/^\s+|\s+$//g;
+ return $str;
+}
+
+my @module_library_paths = (
+ ".",
+);
+my %modules;
+
+sub parse_module_file {
+ my $src = shift;
+
+ my %module;
+
+
+ my $last_proc_type = "null";
+ my $last_input = "null";
+ my $last_output = "null";
+ foreach my $line ( split("\n", $src) ) {
+ chomp $line;
+ if ( $line =~ m/^#/ ) {
+ next;
+ }
+
+ if ( $line =~ m/^Manufacturer:(.*)/ ) {
+ my $manu = $1;
+ $manu = trim($manu);
+ $module{'Manufacturer'} = $manu;
+ }
+
+ if ( $line =~ m/^Module:(.*)/ ) {
+ my $mod = $1;
+ $mod = trim($mod);
+ $module{'Module'} = $mod;
+ }
+
+ if ( $line =~ m/^Revision:(.*)/ ) {
+ my $rev = $1;
+ $rev = trim($rev);
+ $module{'Rev'} = $rev;
+ }
+
+ if ( $line =~ m/^-\ / ) {
+ if ( $line =~ m/^-\ Input:(.*)/ ) {
+ my $input = $1;
+ $input = trim($input);
+ my %input_chars;
+ $last_input = $input;
+ $last_proc_type = "input";
+ $module{'Inputs'}->{$input} = \%input_chars;
+ }
+
+ if ( $line =~ m/^-\ Knob:(.*)/ ) {
+ my $knob = $1;
+ $knob = trim($knob);
+ my %knob_chars;
+ $last_input = $knob;
+ $last_proc_type = "input";
+ $module{'Inputs'}->{$knob} = \%knob_chars;
+ }
+
+ if ( $line =~ m/^-\ Output:(.*)/ ) {
+ my $output = $1;
+ $output = trim($output);
+ my %output_chars;
+ $last_output = $output;
+ $last_proc_type = "output";
+ $module{'Outputs'}->{$output} = \%output_chars;
+ }
+ }
+
+ if ( $line =~ m/^--\ / ) {
+ if ( $line =~ m/^--\ Position:(.*)/ ) {
+ my $pos_args = $1;
+ $pos_args = trim($pos_args);
+ if ( $last_proc_type eq "input" ) {
+ $module{'Inputs'}->{$last_input}->{'pos'} = $pos_args;
+ } elsif ( $last_proc_type eq "output" ) {
+ $module{'Outputs'}->{$last_output}->{'pos'} = $pos_args;
+ }
+ }
+ }
+
+ }
+
+ return \%module;
+}
+
+my %AST;
+
+my %PARSE_TABLE = (
+ 'comment' => '^#.*$',
+ 'title' => '^Title: (.*)$',
+ 'mod_path' => '^ModuleDir\ "(.*)"$',
+ 'import' => '^import (Module)::([a-zA-Z0-9]{1,})::([a-zA-Z0-9]{1,})(.*$)',
+ 'set' => '^set\ (.*)$',
+);
+
+my %PARSE_RULES = (
+ 'comment' => sub {},
+ 'title' => sub {
+ my $title = shift;
+ $AST{'Title'} = $title;
+ },
+ 'mod_path' => sub {
+ my $file_path = shift;
+ if ( ! -d $file_path ) {
+ die "Path: $file_path doesn't look like a directory, exiting";
+ }
+
+ push(@module_library_paths, $file_path);
+ },
+ 'import' => sub {
+ my $module_path = shift;
+ my @module_files = sub {
+ my @files;
+ foreach my $path ( @module_library_paths ) {
+ my @f = split("\n", `find $path`);
+ foreach my $file ( @f ) {
+ if ( $file =~ m/.module$/ ) {
+ push(@files, $file);
+ }
+ }
+ }
+ return @files;
+ }->();
+ foreach my $mod_file ( @module_files ) {
+ my $mod_file_content = read_to_var($mod_file);
+ my $mod_ref = parse_module_file($mod_file_content);
+
+ foreach my $ref ( @{$AST{'Modules'}} ) {
+ if ( $$mod_ref{'Manufacturer'} eq $$ref{'Manufacturer'} &&
+ $$mod_ref{'Rev'} eq $$ref{'Rev'} ) {
+ # We've already imported this module
+ next;
+ } else {
+ push(@{$AST{'Modules'}}, $mod_ref);
+ }
+ }
+ }
+ },
+ 'set' => sub { print("$_[0]\n"); },
+);
+
+# Basic line parser
+sub line_parse($) {
+ my $line = shift;
+ my $line_type = "null";
+ my @line_caps;
+ foreach my $key ( keys %PARSE_TABLE ) {
+ if ( $line =~ m/$PARSE_TABLE{$key}/ ) {
+ $line_type = $key;
+ }
+ }
+
+ if ( $line_type ne "null" && $line =~ m/$PARSE_TABLE{$line_type}/) {
+ if ( defined $1 ) {
+ $PARSE_RULES{$line_type}->($1);
+ } else {
+ $PARSE_RULES{$line_type}->();
+ }
+ } else {
+ print("$line\n");
+ }
+}
+
+foreach my $line ( split("\n", $src_content) ) {
+ chomp $line;
+ if ( $line eq "" ) {
+ next;
+ }
+ line_parse($line);
+}
+
+
+print Dumper %AST;