#! /usr/bin/env bqn

help_pre ← 1↓"
Compile Singeli program(s).
Argument is a list of input files and options. Supported:"

short‿long‿args‿dup‿desc ← <˘⍉> ⟨
  "h" ‿"help"  ‿0‿1‿"Print this message and exit"
  "o" ‿"out"   ‿1‿0‿"Output file (print to stdout by default)"
  "oe"‿"errout"‿1‿0‿"Error to: stderr (default), stdout, none, file=path, bqn"
  "os"‿"show"  ‿1‿0‿"show{} to: stdout (default), stderr, none, file=path"
# "?" ‿"stdin" ‿0‿0‿"Use stdin as input, after any argument files"
  "r" ‿"run"   ‿1‿1‿"Use this argument as source code"
  "t" ‿"target"‿1‿0‿"Output type: c (default), cpp, ir"
  "a" ‿"arch"  ‿1‿2‿"Architecture features: list, or none, native (default), all"
  "i" ‿"infer" ‿1‿0‿"Type of architecture inference: strict, or loose (default)"
  "l" ‿"lib"   ‿1‿2‿"Library paths: lib=path to try path/x for include 'lib/x'"
  "c" ‿"config"‿1‿2‿"Configuration: name=value to set config name to value"
  "p" ‿"pre"   ‿1‿0‿"Preamble placed before C output"
  "n" ‿"name"  ‿1‿0‿"Prefix for names in C output"
  "d" ‿"deplog"‿1‿0‿"Output file for log of included dependencies"
⟩
short‿long ∾˜¨⟜<¨↩ "-"‿"--"
args ∾↩ 0 ⋄ dup ∾↩ 1

Spl ← (⊢-˜+`×¬)∘=⊔⊢

c ← ≠short
op ← (short⊸⊐ ⌊ long⊸⊐) •args
op ⌈↩ c ×¬ <`⊸= op⊏args
opts ← ((1+c)∾˜f/op) ⊔ ((op=c)(1-˜×⟜(+`))○(∾⟜1)f←¬0»op⊏args) ⊔ •args
"Option can't be duplicated" ! ∧´ (1≤dup) ≥ 1<≠¨opts
olist ← (2=dup) (∾','⊸Spl¨)⍟⊣¨ (1⌾(¯1⊸⊑)args) ⊣◶⟨0<≠∘⊢,⊑¨⊢⟩¨ opts
help‿out‿oe‿os‿run‿target‿feats‿inf‿lib‿config‿pre‿namepre‿deplog‿files ← olist

{ help ?
  opt_help ← ∾¨´ ⟨desc⟩ ∾˜ (1+·⌈´≠¨)⊸(↑¨)¨ short‿long ∾¨¨ ",:"
  •Out ∾∾⟜(@+10)¨ ⟨help_pre,""⟩ ∾ opt_help
  •Exit@
;@}

_choices ← {∧´𝕨∊𝕩? (⊑𝕩)⊣´𝕨; !∾⟨"Unknown ",𝕗," option: ",∾𝕨," (options are",1↓∾", "⊸∾¨𝕩,")"⟩}
target "target"   _choices↩ "c"‿"cpp"‿"ir"
inf    "inference"_choices↩ "loose"‿"strict"

Rel ← •wdpath⊸•file.At
files Rel¨↩

SplitEq ← (»⊸(⊣-<)·∨`'='⊸=)⊸⊔
libpaths ← (Rel⌾(1⊸⊑) ¯2 ↑ SplitEq)¨ lib
configs ← (2 ↑ SplitEq)¨ config
OutBuf ← {𝕊: e←⟨⟩ ⋄ Save⇐{e∾↩<𝕩⋄𝕩} ⋄ Get⇐{𝕊:e}}
_getShows ← {name _𝕣 𝕩:
  Save‿Get ← OutBuf@
  Out‿Write ← ⊢‿⊢ »˜ {
    "stderr": •term.ErrRaw•ToUTF8∾(@+10)˙ ; "stdout":•Out ; "none":⊢ ;
    "bqn":"error"≡name? ⊢ ;
    (p←"file=")(⊣≡≠⊸↑)𝕩? f←Rel p≠⊸↓𝕩 ⋄ ⊢‿{f •file.Chars ∾∾⟜(@+10)¨𝕩 ⋄ 𝕩} ;
    !"Unknown "∾name∾" output option: "∾𝕩
  }𝕩
  ⟨Out∘Save, Write∘Get⟩
}
⟨ShowOut,ShowWrite⟩ ← "show{}" _getShows      "stdout" ⊣´ os
⟨ErrOut, ErrWrite⟩  ← "error"  _getShows oe ↩ "stderr" ⊣´ oe
Writes ← ShowWrite ⋈ {⋈∾∾⟜(@+10)¨𝕩}⍟(0<≠)∘ErrWrite
⟨ErrExit,_withErr⟩ ← { "bqn"≡oe ? ⟨!, {𝔽⎊@}⟩ ; ⟨•Exit∘1 ⊣ Writes, {𝔽}⟩ }
DepOut‿DepWrite ← {
  wr ← {⟨⟩:⊢; ⟨p⟩: (Rel p)⊸•file.Lines} deplog
  Save‿Get ← OutBuf@ ⋄ DepOut⇐Save ⋄ DepWrite⇐Wr∘Get
}

arch ← ⟨feats,"strict"≢inf⟩ •Import "arch.bqn"
outputs ← ShowOut‿ErrOut‿ErrExit‿DepOut
frontend ← arch‿libpaths‿configs‿outputs •Import "singeli.bqn"
backend ← {
  "ir"≡target ? ⊢ ;
  par ← ⟨"cpp"≡target,arch,"si"⊣´namepre,outputs⟩
  pre ⊑⊸{𝕨⊸𝕏}⍟(0<≠∘⊣) par •Import "emit_c.bqn"
}
Output ← {
  ≠out ? (Rel⊑out) •file.Chars ⊢ ;
  •Out⍟(0<≠) ¯1⊸↓
}⊸⊢
Result ← {show‿errout‿deplog‿out⇐𝕩} Writes ∾ DepWrite ⋈ ⊢

Result {Output Backend ∾ Frontend¨ 𝕩}_withErr (<¨run) ∾ files
