diff options
Diffstat (limited to '')
-rw-r--r-- | documentation/book/the_lux_programming_language/chapter_11.md | 180 |
1 files changed, 73 insertions, 107 deletions
diff --git a/documentation/book/the_lux_programming_language/chapter_11.md b/documentation/book/the_lux_programming_language/chapter_11.md index a2d603ca0..e3e73d97a 100644 --- a/documentation/book/the_lux_programming_language/chapter_11.md +++ b/documentation/book/the_lux_programming_language/chapter_11.md @@ -18,15 +18,13 @@ For starters, it's the home of the (_code_) `Parser` type: ```clojure (type: .public Parser - {#.doc "A Lux code parser."} (//.Parser (List Code))) ``` -Which is based on the `Parser` type from the `library/lux/control/parser` module: +Which is based on the _generic_ `Parser` type from the `library/lux/control/parser` module: ```clojure (type: .public (Parser s a) - {#.doc "A generic parser."} (-> s (Try [s a]))) ``` @@ -41,21 +39,21 @@ There are many such code-parsers (and combinators) in the `library/lux/control/p Then, in the `library/lux/macro/syntax` module, there is a mechanism for defining macros: the `syntax:` macro. ```clojure -"A more advanced way to define macros than 'macro:'." -"The inputs to the macro can be parsed in complex ways through the use of syntax parsers." -"The macro body is also (implicitly) run in the Meta monad, to save some typing." -"Also, the compiler state can be accessed through the *compiler* binding." -(syntax: .public (object {.let [imports (class_imports *compiler*)]} - {.let [class_vars (list)]} - {super (opt (super_class_decl^ imports class_vars))} - {interfaces (tuple (some (super_class_decl^ imports class_vars)))} - {constructor_args (constructor_args^ imports class_vars)} - {methods (some (overriden_method_def^ imports))}) - (let [def_code ($_ text\compose "anon-class:" +... A more advanced way to define macros than 'macro:'. +... The inputs to the macro can be parsed in complex ways through the use of syntax parsers. +... The macro body is also (implicitly) run in the Meta monad, to save some typing. +... Also, the compiler state can be accessed through the *lux* binding. +(syntax: .public (object [.let [imports (class_imports *lux*) + class_vars (list)] + super (opt (super_class_decl^ imports class_vars)) + interfaces (tuple (some (super_class_decl^ imports class_vars))) + constructor_args (constructor_args^ imports class_vars) + methods (some (overriden_method_def^ imports))]) + (let [def_code ($_ text#composite "anon-class:" (spaced (list (super_class_decl$ (maybe.else object_super_class super)) - (with_brackets (spaced (list\map super_class_decl$ interfaces))) - (with_brackets (spaced (list\map constructor_arg$ constructor_args))) - (with_brackets (spaced (list\map (method_def$ id) methods))))))] + (with_brackets (spaced (list#each super_class_decl$ interfaces))) + (with_brackets (spaced (list#each constructor_arg$ constructor_args))) + (with_brackets (spaced (list#each (method_def$ id) methods))))))] (in (list (` ((~ (code.text def_code)))))))) ``` @@ -63,9 +61,11 @@ Then, in the `library/lux/macro/syntax` module, there is a mechanism for definin The difference between `macro:` and `syntax:` is that `syntax:` allows you to parse, in a structured manner, the inputs to your macro, thereby reducing considerably the complexity necessary for making _big_ macros. -Also, because you're using code-parsers for the hard work, you can write reusable parsers that you can share throughout your macros, if you want to have common syntax. You can even compose your parsers, or use parsers from someone else's library. +Also, because you're using code-parsers for the hard work, you can write reusable parsers that you can share throughout your macros, if you want to have common syntax. - There are already small modules under `library/lux/macro/syntax/` which house some reusable code-parsers and code-generators. +You can even compose your parsers, or use parsers from someone else's library. + + There are already some small modules under `library/lux/macro/syntax/` which house some reusable code-parsers and code-generators. Additionally, `syntax:` binds the `Lux` value on a variable called `*lux*`, so you can use it during your parsing. @@ -76,44 +76,44 @@ Here is an example: ```clojure ... Taken from library/lux/math/infix. -(.module: - {#.doc "Common mathematical constants and functions."} +(.using [library - [lux #* + [lux "*" [abstract - [monad (#+ do)]] + [monad {"+" do}]] [control - ["<>" parser ("#\." functor) - ["<.>" code (#+ Parser)]]] + ["<>" parser ("[1]#[0]" functor) + ["<[0]>" code {"+" Parser}]]] [data - ["." product] + ["[0]" product] [collection - ["." list ("#\." fold)]]] + ["[0]" list ("[1]#[0]" mix)]]] [macro - [syntax (#+ syntax:)] - ["." code]] + [syntax {"+" syntax:}] + ["[0]" code]] [math [number ["n" nat] ["i" int]]]]]) -(type: #rec Infix - (#Const Code) - (#Call (List Code)) - (#Unary Code Infix) - (#Binary Infix Code Infix)) +(type: Infix + (Rec Infix + (Variant + {#Const Code} + {#Call (List Code)} + {#Unary Code Infix} + {#Binary Infix Code Infix}))) (def: literal (Parser Code) ($_ <>.either - (<>\map code.bit <code>.bit) - (<>\map code.nat <code>.nat) - (<>\map code.int <code>.int) - (<>\map code.rev <code>.rev) - (<>\map code.frac <code>.frac) - (<>\map code.text <code>.text) - (<>\map code.identifier <code>.identifier) - (<>\map code.tag <code>.tag))) + (<>#each code.bit <code>.bit) + (<>#each code.nat <code>.nat) + (<>#each code.int <code>.int) + (<>#each code.rev <code>.rev) + (<>#each code.frac <code>.frac) + (<>#each code.text <code>.text) + (<>#each code.symbol <code>.symbol))) (def: expression (Parser Infix) @@ -122,29 +122,15 @@ Here is an example: ..literal (<code>.form (<>.many <code>.any)) (<code>.tuple (<>.and <code>.any expression)) - (<code>.tuple ($_ <>.either - (do <>.monad - [_ (<code>.this! (' #and)) - init_subject expression - init_op <code>.any - init_param expression - steps (<>.some (<>.and <code>.any expression))] - (in (product.right (list\fold (function (_ [op param] [subject [_subject _op _param]]) - [param [(#Binary _subject _op _param) - (` and) - (#Binary subject op param)]]) - [init_param [init_subject init_op init_param]] - steps)))) - (do <>.monad - [init_subject expression - init_op <code>.any - init_param expression - steps (<>.some (<>.and <code>.any expression))] - (in (list\fold (function (_ [op param] [_subject _op _param]) - [(#Binary _subject _op _param) op param]) - [init_subject init_op init_param] - steps))) - )) + (<code>.tuple (do <>.monad + [init_subject expression + init_op <code>.any + init_param expression + steps (<>.some (<>.and <code>.any expression))] + (in (list#mix (function (_ [op param] [_subject _op _param]) + [{#Binary _subject _op _param} op param]) + [init_subject init_op init_param] + steps)))) ))) ``` @@ -156,64 +142,43 @@ And here are some examples of syntax macros: (def: (prefix infix) (-> Infix Code) (case infix - (#Const value) + {#Const value} value - (#Call parts) + {#Call parts} (code.form parts) - (#Unary op subject) + {#Unary op subject} (` ((~ op) (~ (prefix subject)))) - (#Binary left op right) + {#Binary left op right} (` ((~ op) (~ (prefix right)) (~ (prefix left)))))) -(syntax: .public (infix {expr ..expression}) - {#.doc (example "Infix math syntax." - (infix [x i.* +10]) - (infix [[x i.+ y] i.* [x i.- y]]) - (infix [sin [x i.+ y]]) - (infix [[x n.< y] and [y n.< z]]) - (infix [#and x n.< y n.< z]) - (infix [(n.* 3 9) gcd 450]) - - "The rules for infix syntax are simple." - "If you want your binary function to work well with it." - "Then take the argument to the right (y) as your first argument," - "and take the argument to the left (x) as your second argument.")} +(syntax: .public (infix [expr ..expression]) (in (list (..prefix expr)))) ``` ```clojure -(syntax: .public (^sequence& {patterns (<code>.form (<>.many <code>.any))} - body - {branches (<>.some <code>.any)}) - {#.doc (example "Allows destructuring of sequences in pattern-matching expressions." - "Caveat emptor: Only use it for destructuring, and not for testing values within the sequences." - (let [(^sequence& x y z _tail) (some_sequence_func +1 +2 +3)] - (func x y z)))} - (with_identifiers [g!sequence] - (let [body+ (` (let [(~+ (list\join (list\map (function (_ pattern) - (list (` [(~ pattern) (~ g!sequence)]) - (` ((~! //.result) (~ g!sequence))))) - patterns)))] +(syntax: .public (^stream& [patterns (<code>.form (<>.many <code>.any)) + body <code>.any + branches (<>.some <code>.any)]) + (with_symbols [g!stream] + (let [body+ (` (let [(~+ (|> patterns + (list#each (function (_ pattern) + (list (` [(~ pattern) (~ g!stream)]) + (` ((~! //.result) (~ g!stream)))))) + list#conjoint))] (~ body)))] - (in (list& g!sequence body+ branches))))) + (in (list& g!stream body+ branches))))) ``` ```clojure -(syntax: .public (cond> {_ _reversed_} - prev - {else body^} - {_ _reversed_} - {branches (p.some (p.and body^ body^))}) - {#.doc (example "Branching for pipes." - "Both the tests and the bodies are piped-code, and must be given inside a tuple." - (|> +5 - (cond> [i.even?] [(i.* +2)] - [i.odd?] [(i.* +3)] - [(new> -1 [])])))} - (with_identifiers [g!temp] +(syntax: .public (cond> [_ _reversed_ + prev <code>.any + else body^ + _ _reversed_ + branches (<>.some (<>.and body^ body^))]) + (with_symbols [g!temp] (in (list (` (let [(~ g!temp) (~ prev)] (cond (~+ (do list.monad [[test then] branches] @@ -231,6 +196,7 @@ This may be a short chapter, but not because its subject is small. The opportunities that code-parsers open are fantastic, as it puts within your reach macros which would otherwise be much harder to implement correctly. Don't worry about complex inputs: your macros can implement entire new embedded programming languages if you want them to. + Code-parsers can generate any data-type you want, so you can easily translate the information in the input syntax to whatever data-model you need. But, now that we've spent 3 chapters about metaprogramming in Lux, I think it's fair that we clear our minds a little by looking at other subjects. |