hara.common
Shared grammar, emit, and preprocess layers.
hara.common holds reusable grammar, emission, preprocess, rewrite, and utility functions shared by target language models.
1 Motivation
Target models should not each reinvent expression emission, assignment handling, function rendering, top-level forms, or preprocessing. hara.common keeps those concerns reusable.
2 Walkthrough
2.1 Symbol utilities
Target language models work with a lot of symbols. hara.common.util provides small helpers for decomposing and recombining qualified symbols, as well as converting between idiomatic naming styles.
decompose and recompose symbols
(util/sym-id 'L.core/identity)
=> 'identity
(util/sym-module 'L.core/identity)
=> 'L.core
(util/sym-pair 'L.core/identity)
=> '[L.core identity]
(util/sym-full 'L.core 'identity)
=> 'L.core/identity
convert between dash and underscore naming
(util/sym-default-str :hello-world)
=> "hello_world"
(util/sym-default-inverse-str "hello_world")
=> "hello-world"
2.2 Language contexts and pointers
When emitting code for a target language, the model needs to know which runtime context to use. lang-context turns a keyword into a namespaced lang keyword, and lang-pointer builds a pointer that the runtime can resolve.
create a language context
(util/lang-context :lua)
=> :lang/lua
create a language pointer
(into {} (util/lang-pointer :lua {:module 'L.core}))
=> {:context :lang/lua
:module 'L.core
:lang :lua
:context/fn #'hara.common.util/lang-rt-default}
2.3 Building grammars
The grammar is the dictionary that maps xtalk operators to target-language emission rules. hara.common.grammar collects operator definitions from spec and macro namespaces and lets you build a concrete operator table.
list grammar categories
(take 5 (grammar/ops-list))
=> '(:builtin :builtin-global :builtin-module :builtin-helper :free-control)
build a grammar for a category
(keys (grammar/build :include [:vars]))
=> '(:seteq :var)
(grammar/ops-summary [:counter])
=> '([:counter {:incby #{:+=}, :decby #{:-=}, :mulby #{:*=}, :incto #{:++}, :decto #{:--}}])
construct a grammar object
(grammar/grammar? (grammar/grammar :test
(grammar/to-reserved (grammar/build-min))
{}))
=> true
2.4 Emit helpers
hara.common.emit-helper contains low-level helpers used by the emitter, such as classifying forms and reading grammar options.
classify forms
(emit-helper/form-key-base :a)
=> [:keyword :token true]
(emit-helper/form-key-base 'x)
=> [:symbol :token true]
(emit-helper/form-key-base [1 2])
=> [:vector :data]
read default grammar options
(select-keys (emit-helper/get-options emit-helper/+default+ [:data :map-entry])
[:assign :keyword])
=> {:assign ":" :keyword :string}
quote a string for single-quoted targets
(emit-helper/pr-single "hello")
=> "'hello'"
2.5 Provenance tracking
As code passes through preprocessing and emission, errors need to be traced back to their source. hara.common.provenance extracts line numbers and module context from forms and merges provenance frames.
extract line information
(provenance/line-of (with-meta '(+ 1 2) {:line 5}))
=> 5
build a provenance frame
(provenance/provenance {:hara/module {:id 'L.core}
:hara/namespace 'documentation.hara-common
:hara/form (with-meta '(+ 1 2) {:line 5})})
=> '{:hara/module L.core
:hara/namespace documentation.hara-common
:hara/form (+ 1 2)
:hara/line 5}
merge provenance into options
(-> {:lang :lua}
(provenance/with-provenance {:hara/phase :emit/direct}
{:hara/module 'L.core})
:hara/provenance)
=> '{:hara/phase :emit/direct
:hara/module L.core}