Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General interface #99

Merged
merged 37 commits into from
Mar 24, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
c223ce0
oll: Improve initialization code
uliska Mar 9, 2015
86f7762
oll: New command \useLibrary
uliska Mar 9, 2015
907102d
oll: deprecate \loadModule for loading libraries
uliska Mar 9, 2015
b368374
oll: add \declareLibrary (WIP)
uliska Mar 10, 2015
20336bf
oll: add oll-maintainers? predicate and use it
uliska Mar 10, 2015
971b708
oll: add 'version as mandatory library option
uliska Mar 10, 2015
bcc0fb5
oll: add 'short-description as mandatory option
uliska Mar 10, 2015
e0c9595
oll: add descriptions as mandatory options
uliska Mar 10, 2015
88c7462
oll: Add type-checked known library options
uliska Mar 10, 2015
66cd472
oll: "declare" stylesheets library
uliska Mar 10, 2015
f1ff7c1
oll: Small comment polishing
uliska Mar 10, 2015
a8cc3a8
comptools: \declareLibrary
uliska Mar 10, 2015
f9bf8d9
scholarly: \declareLibrary
uliska Mar 10, 2015
65506fc
oll: paths in os-path can also be symbol? paths
uliska Mar 10, 2015
0b417c0
oll: factor out extract-options
uliska Mar 10, 2015
b75dfcd
oll: use "\include" instead of (ly:gulp-file)
uliska Mar 10, 2015
9472599
oll: add \useModule
uliska Mar 10, 2015
99265a1
oll: completely deprecate \loadModule
uliska Mar 10, 2015
4b4fdca
oll: ensure library name to be used as lowercase
uliska Mar 10, 2015
aa2226a
oll: include init file (if present) in \useModule
uliska Mar 10, 2015
370aac1
oll: once more use \include instead of gulp-file
uliska Mar 10, 2015
ab5b618
utility: Add (empty) main and init files
uliska Mar 10, 2015
dc3feec
scholarly: Remove old module loading code
uliska Mar 10, 2015
5bbbb94
stylesheets: Replace gulp-file with \include
uliska Mar 11, 2015
942ce16
Merge remote-tracking branch 'origin/master' into general-interface
uliska Mar 19, 2015
827ee82
basic-example: Update to new calling syntax
uliska Mar 19, 2015
cee7c87
oll: Minor output improvement in \useModule
uliska Mar 19, 2015
89cd692
scholarly: Update usage-examples to new syntax
uliska Mar 19, 2015
d20149e
gridly: Remove redundant includes from examples
uliska Mar 19, 2015
7002229
gridly: Remove unnecessary test file
uliska Mar 19, 2015
533eaca
gridly: Update usage-examples to new syntax
uliska Mar 19, 2015
3378fdb
comptools: Update usage-examples to new syntax
uliska Mar 19, 2015
2e9afcf
comptools: Update unit-tests to new syntax
uliska Mar 19, 2015
7ead7cb
Revert "gridly: Remove unnecessary test file"
uliska Mar 20, 2015
897d4a2
Revert "gridly: Remove redundant includes from examples"
uliska Mar 20, 2015
377d6ac
gridly: But update syntax then
uliska Mar 20, 2015
95dfea4
oll: rename dot-path->string to symbol-list->dot-path
uliska Mar 24, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions ly/_internal/init-openlilylib.ily
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@
% Set the root path of openLilyLib
% - for oll module inclusion
% - for Scheme module inclusion
% This must be called from the main openlilylib file
% because that's inside the desired root directory
setRootPath =
#(define-void-function (parser location)()
(let* ((path (location-extract-path location)))
#{ \registerOption global.root-path #path #}
(if (not (member path %load-path))
(set! %load-path `(,path ,@%load-path)))))

(let* ((path
(normalize-path
(string-append
(location-extract-path location)
"/.."))))
#{ \registerOption global.root-path #path #}))
\setRootPath

% Functionality to load and manage modules
\include "module-handling.ily"
Expand Down
284 changes: 284 additions & 0 deletions ly/_internal/module-handling.ily
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,283 @@
#(define oll-loaded-libraries '())
#(define oll-loaded-modules '())

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Helper tools

% Extract an options alist from a context-mods argument
% Return an empty list if no mods are passed.
#(define (extract-options ctx-mods)
(if ctx-mods
(map (lambda (o)
(cons (cadr o) (caddr o)))
(ly:get-context-mods ctx-mods))
'()))


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Predicates for type-checking of library options

% Simple regex check for Name plus email address in angled brackets:
% "Ben Maintainer <[email protected]>"
#(define (oll-maintainer? obj)
(let ((pat (make-regexp ".*<.*@.*>")))
(if (and (string? obj)
(regexp-exec pat obj))
#t #f)))

% Returns true for one maintainer or a list of them
#(define (oll-maintainers? obj)
(or (oll-maintainer? obj)
(and (list? obj)
(every oll-maintainer? obj))))

% Returns true if obj is a string representation of an integer
#(define (integer-string? obj)
(integer? (string->number obj)))

% Returns true if a string is a three-element dot-joined list of integers
#(define (oll-version-string? obj)
(and (string? obj)
(let ((lst (string-split obj #\.)))
(and (= 3 (length lst))
(every integer-string? lst)))))

% Alist with mandatory options for library declarations
% Each entry is a pair of option name symbol and type predicate
#(define oll-lib-mandatory-options
`((maintainers . ,oll-maintainers?)
(version . ,oll-version-string?)
(short-description . ,string?)
(description . ,string?)
))

% Alist with recognized options for library declarations
% If an option is in this list it is type-checked against the given predicate.
#(define oll-lib-known-options
`((lilypond-min-version . ,oll-version-string?)
(lilypond-max-version . ,oll-version-string?)
))


% Declare a library, to be done in the __init__.ily file
% Arguments:
% - display-name: The official name of the library
% - name (optional): the directory name of the library
% This name must be 'symbol?' compatible, i.e. must consist of
% alphabetical characters and hyphens only.
% This argument can be omitted if the display-name is the same
% as the directory name with exception of capitalization.
% (e.g. when the display-name is "ScholarLY" the implicit 'name'
% is "scholarly").
% - options: a \with {} clause with metadata options.
% some of them are mandatory, others can be used at the discretion
% of the library maintainers:
% For possible mandatory and known options see the two lists above.
%
declareLibrary =
#(define-void-function (parser location display-name name options)
(string? (symbol?) ly:context-mod?)
(let*
;; internal-name is either explicitly given
;; or the lowercase version of display-name
((internal-name
(or name (string-downcase display-name)))
;; option path to the library's meta options
(meta-path `(,(string->symbol internal-name) meta))
;; retrieve options from context mods
(options (extract-options options)))

;; initialize library's meta option branch
#{ \registerOption #meta-path #'() #}

;; check if all mandatory options are present
(for-each
(lambda (o)
(let ((mand-opt (car o)))
(if (not (assoc-ref options mand-opt))
(oll:error (format "
Missing option in library declaration!
Library: \"~a\"
Option: \"~a\"" display-name mand-opt) ""))
))
oll-lib-mandatory-options)

;; process options, type-check mandatory options and store in meta
(for-each
(lambda (o)
(let* ((opt-name (car o))
(opt-val (cdr o))
(predicate? (assoc-ref oll-lib-mandatory-options opt-name))
(known-opt-pred? (assoc-ref oll-lib-known-options opt-name)))
;; check for type if there is a predicate (-> true for mandatory options)
(if (and predicate?
(not (predicate? opt-val)))
(oll:error (format "
Type check failed for mandatory option in library declaration!
Library: \"~a\"
Option: \"~a\"
Predicate: ~a" display-name opt-name predicate?) ""))
(if (and known-opt-pred?
(not (known-opt-pred? opt-val)))
(oll:error (format "
Type check failed for known option in library declaration!
Library: \"~a\"
Option: \"~a\"
Predicate: ~a" display-name opt-name known-opt-pred?) ""))

;; store option
#{ \setChildOption #meta-path #opt-name #opt-val #}
))
options)))


% Initialize a library before first use.
% This also serves as a kind of declaration of the intent of using it.
% If options are passed in a \with {} clause they are set after in
% initialization file has been loaded. If the initializiation did not
% register the options (in the form LIBRARY.OPTION) this will cause
% warnings about trying to set unregistered options.
useLibrary =
#(define-void-function (parser location options name)
((ly:context-mod?) symbol? )
(let*
;; ensure the library name is lowercase
((display-name name)
(name (string->symbol (string-downcase (symbol->string name)))))
"Load an openLilyLib library and initialize it"
(if (not (member name oll-loaded-libraries))
;; Determine paths to init and main files
(let* ((lib-dir
(string-append
#{ \getOption global.root-path #}
"/" (symbol->string name) "/"))
(init-file
(string-append lib-dir "__init__.ily"))
(main-file
(string-append lib-dir "__main__.ily")))

;; Create a root option for the library
#{ \registerOption #(list name) #'() #}

;; Load initialization file if it exists
(if (file-exists? init-file)
(begin
(oll:log location "Initialize library \"~a\" ..." display-name)
(ly:parser-include-string parser
(format "\\include \"~a\"" init-file))))

;; If a \with clause has been given pass the options to the library.
;; If the options have not been registered in the __init__ file this
;; will trigger oll:warn messages but don't abort the job.
(if options
(for-each
(lambda (o)
(let ((opt-path (list name (cadr o)))
(opt-val (caddr o)))
#{ \setOption #opt-path #opt-val #}))
(ly:get-context-mods options)))

;; load the main file of the library or issue a warning if that isn't found.
(if (file-exists? main-file)
(begin
; (ly:parser-include-string parser (ly:gulp-file main-file))
(ly:parser-include-string parser
(format "\\include \"~a\"" main-file))
(set! oll-loaded-libraries
(append oll-loaded-libraries
`(,name)))
(oll:log "... completed." ""))
(oll:warn location (format "Library main file \"~a\" not found" main-file)))))))


% Load a module from within a library.
% A module is either a single .ily file or a __main__.ily file in a folder.
% It is adressed as a dotted path representing the directory structure
% leading to the file. The first element of the path is the library, the last one
% is the name of the module.
% It is looked for files path/to/NAME.ily or path/to/NAME/__main__.ily
%
% An optional \with {} clause can contain options that will be set
% after the module has been loaded. Such options must have been registered
% in the module definition file.

useModule =
#(define-void-function (parser location options sym-path)
((ly:context-mod?) list?)
(let ((dot-path (join-dot-path sym-path)))
;; only do any work if the module isn't already loaded
(if (member dot-path oll-loaded-modules )
(oll:warn location
(format "Module already loaded. Skipping \"~a\"" dot-path))
(let*
((library (car sym-path))
(mod-path (cdr sym-path))
;; unix file path to the module (base)
(module-basename
(string-append
#{ \getOption global.root-path #}
"/" (join-unix-path sym-path)))
;; Check if a valid file can be found for the module path
;; #f if no file is found
(ext
(or (if (file-exists? (string-append module-basename ".ily"))
".ily" #f)
(if (file-exists? (string-append module-basename "/__main__.ily"))
"/__main__.ily" #f)))
(filename
(if ext
(string-append module-basename ext) #f))
(init-file
(and ext
(string=? "/__main__.ily" ext)
(let ((fname (string-append module-basename "/__init__.ily")))
(if (file-exists? fname)
fname #f))))
(opts (extract-options options)))

;; Load module if present
(if filename
;; but only if the library is already loaded
(if (not (member library oll-loaded-libraries))
(oll:warn location
(format "Library \"~a\" must be loaded before module \"~a\""
library (join-dot-path mod-path)))
(begin

;; include init-file if present
(if init-file
(ly:parser-include-string parser
(format "\\include \"~a\"" init-file)))


;; include module file
(ly:parser-include-string parser
(format "\\include \"~a\"" filename))

;; register module
(set! oll-loaded-modules
(append oll-loaded-modules (list dot-path)))

;; pass along options
(for-each
(lambda (o)
#{ \setChildOption #sym-path #(car o) #(cdr o) #})
opts)

;; TODO: COntinue with setting options
))
(oll:warn location
(format "No file found for module ~a"
(join-dot-path (append (list library) mod-path)))))))))



% Conditionally register and load a library when
% for the first time a module from that library is requested.

%%% DEPRECATED !!!
%%% This is deprecated together with \loadModule
%%%
registerLibrary =
#(define-void-function (parser location lib)
(string?)
Expand Down Expand Up @@ -66,6 +340,15 @@ registerLibrary =
loadModule =
#(define-void-function (parser location path)(string?)
"Load an openLilyLib module if it has not been already loaded."

;; DEPRECATION !!!
(oll:warn location
"\n \\loadModule
is deprecated and will eventually be removed.
Please use the more idiomatic and powerful
\\useLibrary and
\\useModule now.")

(let*
((path-list (string-split path #\/))
(lib (first path-list))
Expand All @@ -82,6 +365,7 @@ loadModule =
#{ \getOption global.root-path #}
"/"
append-path)))

;; try to load the file if it isn't already present
(if (member load-path oll-loaded-modules)
(oll:log "module ~a already loaded. Skipping." load-path)
Expand Down
2 changes: 1 addition & 1 deletion ly/_internal/options.ily
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ setOption =
#{ \setatree openlilylib-options #opt-path #val #}
(oll:log location "Option set: ~a"
(format "~a: ~a"
(dot-path->string opt-path) val)))
(symbol-list->dot-path opt-path) val)))
;; reject setting unknown options and report that
(oll:warn location "Not a valid option path: ~a" (dot-path->string opt-path))))

Expand Down
2 changes: 1 addition & 1 deletion ly/_internal/utilities/alist-access.ily
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

% Convert a list to a dot-path notation string.
% Can be used to print/log tree paths
#(define (dot-path->string path)
#(define (symbol-list->dot-path path)
"output option path list as a dot-delimited string"
(string-join
(map
Expand Down
7 changes: 7 additions & 0 deletions ly/_internal/utilities/general-predicates.ily
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@
(and (list? obj)
(every string? obj)))

% Returns true if obj is a string or a list of pairs (alist)
% (used for mandatory library options)
#(define (string-or-alist? obj)
(if (or (string? obj)
(and (list? obj)
(every pair? obj)))
#t #f))
18 changes: 12 additions & 6 deletions ly/_internal/utilities/os-path.scm
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,24 @@
Takes either a path string or a list.
If 'path' is a string it is split
respecting the OS dependent path separator,
if it is a list then simply the list is returned."
if it is a list then the list is returned,
while elements are converted from symbol to string if necessary."
(if (string? path)
(string-split path os-path-separator)
path))
(map
(lambda (elt)
(if (string? elt)
elt
(symbol->string elt)))
path)))

(define-public (join-unix-path path-list)
"Returns a Unix formatted path string from a list."
(string-join path-list "/"))
"Returns a Unix formatted path string from a (symbol?/string?) list."
(string-join (split-path path-list) "/"))

(define-public (join-dot-path path)
"Returns a string in dot-notation (to be displayed).
Takes a list with string elements or an
Takes a list with symbol?/string? elements or an
OS independent path string."
(let ((path-list (split-path path)))
(string-join path-list ".")))
Expand All @@ -76,7 +82,7 @@

(define-public (absolute-path? path)
"Test if the given path is absolute.
Process either a string or a string list."
Process either a string or a symbol?/string? list."
(let ((path-list (split-path path)))
(if (and (> (length path-list) 0)
;; consider the path absolute if either the regex for windows volumes is matched
Expand Down
Loading