#+TODO: TODO(t@) TOIMPL(i@) SPEC(r@) BLOCK(b@) | DONE(d!) #+STARTUP: indent logdone logdrawer content # ------------------------------------------------------ #+NOIR_TEMPLATE_URL: https://github.com/noir-lang/noir/blob/%h/%p#L%lC%c-L%lC%s # ------------------------------------------------------ #+TITLE: Noir Treesitter Grammar #+AUTHOR: Jordan Ellis Coppard #+LANGUAGE: en #+OPTIONS: ^:{} All notes currently against upstream commit {{{keyword(NOIR_VERSION)}}}. Noir's syntax isn't officially specified currently; expected given the language is pre 1.0. So, here are notes and links to compiler infrastructure to ascertain language minutia. * Compiler :noirc: :PROPERTIES: :ID: 9405296D-1F11-4E7E-8306-401487D24999 :END: Noir's compiler frontend performs parsing and lexing at the same time; the parser internally lexes the target file(s). The lexer transforms an iterator of characters into an iterator of ~SpannedToken~; each ~Token~ having a ~Span~ (delimited region in source file). ~Spans~ are owned by AST nodes which forms the (initial) parsing result. Concerning Noir's frontend compiler: - Tag =node= being used as a non-terminal symbol. - Tag =leaf= used as a terminal symbol. Entry: [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::pub fn parse_program(source_program: &str)][parse_program]] ** Lexing TODO: Rename this header, idk? *** Code whitespace Whitespace is not significant in Noir. #+transclude: [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs][Lexer::eat_whitespace()]] :lines 703-704 :src rust Newline, tab, carriage return, ASCII space. #+transclude: [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs][Lexer::is_code_whitespace()]] :lines 699-700 :src rust ** Parsing :PROPERTIES: :CUSTOM_ID: h:DB42728E-09AF-4189-B2BE-E48853E5C1D9 :END: Parser entry at [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::pub fn parse_program(source_program: &str)][parse_program()]], yields a ~ParsedModule~ struct. #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/mod.rs::84][ParsedModule struct]] :lines 84-89 :src rust From ~ParsedModule~ we see ~Item~ and ~ItemKind~ which reveals some intial top-level structure: #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/mod.rs::133][ItemKind enum]] :lines 133-145 :src rust *************** TODO Continue exploring parser/mod.rs which has other structs like ParsedSubModule These help inform the general structure for the tree-sitter grammar. Especially now as I am currently trying to get the top-level stuff coarse enough to reach the fine-grained things which have been 'implemented' but which cannot yet be tested since a full CST is required. *************** END ** Structure :bingbong: This subtree layout is for documentation, and does not (in it's raw source form) 1:1 represent Noir's AST as there are many nested, recursive, and cross-referencing nodes (which is to be expected). Org-mode is strictly hierarchical so what is literally in-source here are logical groupings for notes and grammar construction. A graph of the relationship between AST nodes (so showing Noir's AST) can be exported via graphviz (TODO: link and document that custom exporter). *************** TODO This line and the following one review/change as required *************** END The first list under each /Noir/ heading represents grammar where each element (-) is a logic OR and each element (+) is an ordered item type (also inlined via =by=). So, the following example list reads "(A followed by B) OR (C followed by D followed by E)". : - A by B : - C : + D : + E *** DONE Program :node: CLOSED: [2025-05-25 Sun 17:51] :PROPERTIES: :CUSTOM_ID: h:A8A4AE5F-6FDB-4091-87DA-E4BCE320452C :END: :LOGBOOK: - State "DONE" from "TODO" [2025-05-25 Sun 17:51] :END: :pgd: + [[#h:649C4EE8-8F15-4F45-8EA6-3AD48A893930][Module]] :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::pub(crate) fn parse_program(&mut self)][parse_program()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::115][Program doc]] :lines 115-115 :src fundamental *** SPEC Module :node: :PROPERTIES: :CUSTOM_ID: h:649C4EE8-8F15-4F45-8EA6-3AD48A893930 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-05-25 Sun 17:52] :END: :pgd: + [[#h:C58B2CB4-FF62-49BB-AFFD-1BADF4462B9D][InnerDocComments]] + (rep0) [[#h:8CC1D239-66B1-45A9-BB71-66AF07161479][Item]] :end: *************** TODO Have inlined this at the top of rules in the grammar file for now Go back to transclusion if/when appropriate. *************** END This /is/ ~ParsedModule~ (see: [[#h:DB42728E-09AF-4189-B2BE-E48853E5C1D9][Parsing]]). So a Module represents one parsed source-file, but they may appear inline (see: [[#h:9204514E-E5FC-488B-8066-66F0D9AF0C85][ModOrContract]]). Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::pub(crate) fn parse_module(&mut self, nested: bool)][parse_module()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::122][Module doc]] :lines 122-122 :src fundamental *** DONE DocComments CLOSED: [2025-06-05 Thu 16:59] :LOGBOOK: - State "DONE" from "TODO" [2025-06-05 Thu 16:59] - State "TODO" from [2025-05-25 Sun 20:33] \\ Restructure, putting {Inner,Outer}DocComments under this. :END: InnerDocComments and OuterDocComments only differ in the ~DocStyle~ associated (see [[#h:E866B8E1-2736-4A62-938B-0FD416C4A088][comment lexing]]). They are both comprised of [[#h:E866B8E1-2736-4A62-938B-0FD416C4A088][comment]], are both allowed at top-level, and have effectively identical parsing. For brevity, they are parsed generally via: Calls [[#h:AC5A6E2C-59EC-44C6-9D1E-52553E56C3F9][eat_kind]] with ~TokenKind::{Inner,Outer}DocComment~, so matched tokens from lexing are [[#h:155F78B1-495B-4F49-BFED-82369979A23E][line_comment]] and [[#h:5B512D17-7FD5-4FD5-B4AA-C1B27A9E6FCA][block_comment]] so long as they have ~DocStyle::{Inner,Outer}~. A string of the matched comments contents is then returned. The contents (see lexing) exclude the glue-token prefix and (closing suffix for block comments). **** DONE InnerDocComments :leaf: CLOSED: [2025-06-05 Thu 16:57] :PROPERTIES: :CUSTOM_ID: h:C58B2CB4-FF62-49BB-AFFD-1BADF4462B9D :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-05 Thu 16:57] :END: :pgd: + (rep0) [[#h:E866B8E1-2736-4A62-938B-0FD416C4A088][comment]] (token, with ~DocStyle::Inner~) :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/doc_comments.rs::pub(super) fn parse_inner_doc_comments(&mut self)][parse_inner_doc_comments()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/doc_comments.rs::fn parse_inner_doc_comment(&mut self)][parse_inner_doc_comment()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/doc_comments.rs::6][InnerDocComments doc]] :lines 6-6 :src fundamental *************** TODO Look at BorrowedToken Not strictly related to this headline only, just while looking around. In token.rs enum BorrowedToken represents a token in noirs grammar. So, all the token types are there. *************** END **** DONE OuterDocComments :leaf: CLOSED: [2025-06-05 Thu 16:57] :PROPERTIES: :CUSTOM_ID: h:FB5478BF-5E66-4686-931B-733349F83FD8 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-05 Thu 16:57] :END: :pgd: + (rep0) [[#h:E866B8E1-2736-4A62-938B-0FD416C4A088][comment]] (token, with ~DocStyle::Outer~) :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/doc_comments.rs::pub(super) fn parse_outer_doc_comments(&mut self)][parse_outer_doc_comments()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/doc_comments.rs::fn parse_outer_doc_comment(&mut self)][parse_outer_doc_comment()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/doc_comments.rs::19][OuterDocComments doc]] :lines 19-19 :src fundamental *** TODO Item :node: :PROPERTIES: :CUSTOM_ID: h:8CC1D239-66B1-45A9-BB71-66AF07161479 :END: :pgd: + [[#h:FB5478BF-5E66-4686-931B-733349F83FD8][OuterDocComments]] + [[#h:D661B191-E117-4EB9-B6F7-322B7B67A79F][ItemKind]] :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::pub(crate) fn parse_module_items(&mut self, nested: bool)][parse_module_items()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::fn parse_module_item_in_list(&mut self, nested: bool)][parse_module_item_in_list()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::fn parse_item(&mut self)][parse_item()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::87][Item doc]] :lines 87-87 :src fundamental - Parsed without separator (~Parser::parse_module_items~). - ~Parser::parse_module_item_in_list~. **** TODO ItemKind :node:cluster: :PROPERTIES: :CUSTOM_ID: h:D661B191-E117-4EB9-B6F7-322B7B67A79F :END: :pgd: - [[#h:AE3747A8-8CA3-4B6D-AF09-3553CE24CD70][InnerAttribute]] - [[#h:FF901AEF-D40D-44AB-9BA6-C324F3531088][Attribute]] by [[#h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C][Modifiers]] by (grp): - [[#h:ADB184CE-E43C-423B-803F-DE1679A91829][Use]] - [[#h:9204514E-E5FC-488B-8066-66F0D9AF0C85][ModOrContract]] - [[#h:A53DF633-322A-464B-9947-E56B64A112C7][Struct]] - [[#h:954A182F-703F-48FC-85D3-37C11EC959A3][Implementation]] - Trait - [[#h:C21A19F9-DC0C-4F17-9739-B92412D0C4FF][Global]] - TypeAlias - [[#h:B3C4609F-307A-42A1-B420-DBBAB6CDE6E5][Function]] :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::fn parse_item_kind(&mut self)][parse_item_kind()]] *************** TODO Some attribute thing from ages ago idk TODO: Does this mean that in a list of attributes #[foo] #[bar] #![bing] #[bong] that foo and bar are grouped as attributes, the inner attribute bing breaks that group, and then bong is itself in another group later on? Given that inner attribute makes parse_item_kind return early. *************** END #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::97][ItemKind doc]] :lines 97-108 :src fundamental ***** DONE Attributes CLOSED: [2025-06-05 Thu 20:03] :PROPERTIES: :ID: DA6150E7-E986-4749-A481-A95FF1368B74 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-05 Thu 20:03] - State "TODO" from [2025-06-05 Thu 18:56] \\ To contain Attributes and InnerAttribute :END: Calls [[#h:AC5A6E2C-59EC-44C6-9D1E-52553E56C3F9][eat_kind]] with ~TokenKind::{Attribute,InnerAttribute}~, so matched tokens from lexing are [[#h:22DB13A6-D91E-4240-8711-10ED6DEE1C6E][attribute]]. *************** TODO In Noir's AST are these really "declarations"? Organise as appropriate later I don't see such a thing yet, but given how closely Noir is modelled after Rust's syntax I'll bin these as declarations /for now/ *************** END *************** TODO Organise this info from old attributes notes and structure Primary or Secondary; depending on how many can be applied to a function. - Primary: one (alters a functions ACIR output). - Secondary: unlimited. [[file:noir/compiler/noirc_frontend/src/lexer/token.rs][FunctionAttribute]] *************** END *************** TODO Include tag, inner, and primary/secondary as part of tree-sitter parser or nah? *************** END *************** TODO Attribute splits on ( and ) as sub-tokens? *************** END *************** TODO Attributes further captures Attributes have some further captures in the Noir lexer, e.g. `foreign` captures a name afterwards. So do that also (and for the secondary attributes). *************** END ****** DONE InnerAttribute :leaf: CLOSED: [2025-06-05 Thu 20:03] :PROPERTIES: :CUSTOM_ID: h:AE3747A8-8CA3-4B6D-AF09-3553CE24CD70 :END: :LOGBOOK: - State "DONE" from "SPEC" [2025-06-05 Thu 20:03] - State "SPEC" from "TODO" [2025-06-05 Thu 19:10] :END: :pgd: + [[#h:22DB13A6-D91E-4240-8711-10ED6DEE1C6E][attribute]] (token, with ~TokenKind::InnerAttribute~) :end: Parser (sop): [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::if let Some(kind) = self.parse_inner_attribute() {][parse_inner_attribute() parent]] Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/attributes.rs::pub(super) fn parse_inner_attribute(&mut self)][parse_inner_attribute()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/attributes.rs::11][InnerAttribute doc]] :lines 11-11 :src fundamental *************** TODO Relevance of this old comment and associated GH issue? Can be any valid [[SecondaryAttribute]] and is purely a syntactical convenience to apply to it's container versus attribute definitions piled at containers definition. See [[https://github.com/noir-lang/noir/issues/5875][issue]] for more. *************** END ****** DONE Attribute :leaf: CLOSED: [2025-06-05 Thu 20:03] :PROPERTIES: :CUSTOM_ID: h:FF901AEF-D40D-44AB-9BA6-C324F3531088 :END: :LOGBOOK: - State "DONE" from "SPEC" [2025-06-05 Thu 20:03] - State "SPEC" from "TODO" [2025-06-05 Thu 19:16] :END: :pgd: + (rep0) [[#h:22DB13A6-D91E-4240-8711-10ED6DEE1C6E][attribute]] (token, with ~TokenKind::Attribute~) :end: Parser (sop): [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::let attributes = self.parse_attributes();][parse_attributes() parent]] Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/attributes.rs::pub(super) fn parse_attributes(&mut self)][parse_attributes()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/attributes.rs::fn parse_attribute(&mut self)][parse_attribute()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/attributes.rs::20][Attributes doc]] :lines 20-20 :src fundamental In parent all the parsed attributes are collected. ***** SPEC Modifiers :node: :PROPERTIES: :CUSTOM_ID: h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-05-20 Tue 18:06] :END: :pgd: + [[#h:F5A79701-65C9-4FEA-83D8-2413C585A5FA][ItemVisibility]] + (opt) =unconstrained= + (opt) =comptime= + (opt) =mut= :end: #+name: modifiers #+begin_src js // Noirc: Modifiers -- except for visibility (in order). const MODIFIERS = { Unconstrained: 'unconstrained', Comptime: 'comptime', Mut: 'mut', } #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/modifiers.rs::fn parse_modifiers(&mut self, allow_mutable: bool)][parse_modifiers()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/modifiers.rs][Parser::parse_modifiers()]] :lines 17-19 :src rust Remaining keywords consumed as their literal selves: #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/modifiers.rs][unconstrained, comptime, mut]] :lines 32-48 :src rust *************** TODO Verify that the Noir compiler will error if modifiers are supplied out of order, with the exception (mentioned in Noir compiler source) of unconstrained being before pub to support that legacy ordering. *************** END *************** TODO Decide on and note that we do not support legacy unconstrained ordering because it complicates things massively. *************** END *************** TODO Apparently Traits call parse_modifiers Using eglot to show the call hierarchy Traits apparently call this parser, so when we ge to Traits have a looksey I guess. *************** END ***** SPEC ItemVisibility :declaration:leaf: :PROPERTIES: :CUSTOM_ID: h:F5A79701-65C9-4FEA-83D8-2413C585A5FA :END: :LOGBOOK: - State "SPEC" from "DONE" [2025-06-05 Thu 20:14] \\ Don't actually have any tests for this yet - State "DONE" from "TODO" [2024-11-01 Fri 19:56] :END: :pgd: - =pub= (kw) - =pub(crate)= (kw is =crate=) - NIL. :end: #+name: item_visibility #+begin_src js :rule visibility_modifier :arg $ :ast ItemVisibility seq('pub', optional('(crate)')) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/item_visibility.rs::fn parse_item_visibility(&mut self)][parse_item_visibility()]] Missing text (NIL) is a type of visibility in the sense that the default visibility is private if unspecified. Missing text won't affect tree-sitter unless/until qualifying item visibility becomes part of the CST. #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/item_visibility.rs][Parser::parse_item_visibility()]] :lines 9-12 :src rust ***** DONE Use :cluster:node: CLOSED: [2025-06-08 Sun 17:35] :PROPERTIES: :CUSTOM_ID: h:ADB184CE-E43C-423B-803F-DE1679A91829 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-08 Sun 17:35] :END: :pgd: + =use= (kw) + (opt) [[#h:96FCF9AD-3B89-451B-B84D-90A7A625B56D][PathKind]] + [[#h:19EAD845-0134-41CA-85BA-2E09E10D479B][UseTree]] + =;= :end: #+name: use #+begin_src js :rule use_declaration :arg $ :ast Use seq( optional(<>), 'use', // field('tree', $.__use_tree_variants), field('tree', <>), ';', ) #+end_src Parser (sop): [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::if self.eat_keyword(Keyword::Use) {][parse_use_tree() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/use_tree.rs::pub(super) fn parse_use_tree(&mut self)][parse_use_tree()]] Types: [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub struct UseTree {][UseTree struct]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/use_tree.rs::12][Use doc, UseTree doc, UseTreeList doc]] :lines 12-16 :src fundamental [[#h:96FCF9AD-3B89-451B-B84D-90A7A625B56D][PathKind]] is only valid at parse_use_tree and since [[#h:A051D0D5-7007-4DF8-83B7-FB4EFF9C383E][PathNoTurbofish]] already includes attempts to parse PathKind in it. So the doc comment for =Use= is wrong and should be =Use = 'use' PathKind UseTree=. Additionally the doc comment for =UseTree= is wrong since it calls directly to [[#h:7BA3BCB8-65F3-4001-8B02-2F904B014F87][parse_path_after_kind]] which.. skips PathKind. PathNoTurbofish eventually calls down to that same function but only after parsing PathKind, so calling directly to it means only the components after PathKind in PathNoTurbofish are parsed. This has been constructed as a new node [[#h:3E35071C-E186-4562-AC61-1916E637A72E][IdentifiersInPathNoTurbofish]]. It should be =UseTree = IdentifiersInPathNoTurbofish ( '::' '{' UseTreeList? '}' )?=. Only the [[#h:F5A79701-65C9-4FEA-83D8-2413C585A5FA][ItemVisibility]] modifier is applicable, all other [[#h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C][Modifiers]] are parse errors. #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::506][comptime_mutable_and_unconstrained_not_applicable()]] :lines 506-510 :src rust Parsed [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub struct UseTree {][UseTree struct]] is returned as ~ItemKind::Import~ which is [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub fn desugar(self, root: Option, visibility: ItemVisibility)][considered a statement]] however we will narrow and use the declaration sub-category of a statement and name this rule as such. *************** TODO Redo the pgd for these, the treesit grammar is right but pgd is a bit wonky Due to how usetree is parsed its a bit hard to represent in pgd right now, but the tree sitter logic is correct (via all tests so far). *************** END *************** TODO ItemKind docs as appropriate *************** END *************** TODO Upstream commit to fix the doc comment for Use as mentioned in this subheading *************** END ****** DONE UseTree :node: CLOSED: [2025-06-08 Sun 17:35] :PROPERTIES: :CUSTOM_ID: h:19EAD845-0134-41CA-85BA-2E09E10D479B :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-08 Sun 17:35] :END: :pgd: + [[#h:3E35071C-E186-4562-AC61-1916E637A72E][IdentifiersInPathNoTurbofish]] by (opt grp) + =::= + ={= + (opt) [[#h:0C07D74C-165A-4128-BF20-EF7E937F7CAA][UseTreeList]] + =}= :end: #+name: use_tree #+begin_src js :rule __use_tree_variants :arg $ :ast UseTree choice( $.__path_no_kind_no_turbofish, // $.use_list, <>, // XXX: Alias name here needs to match that in __path_no_kind_no_turbofish. // alias($.__use_list_path_prefix, $.path), alias(<>, $.path), // TODO: The structure of how use alias appears in the CST isn't really cognate to use_list.. but can refine this later once the entire grammar is done. // $.use_alias, <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/use_tree.rs::pub(super) fn parse_use_tree_without_kind(][parse_use_tree_without_kind()]] Resulting UseTree kind is either a Path or a List. After call to [[#h:7BA3BCB8-65F3-4001-8B02-2F904B014F87][parse_path_after_kind]] variable ~trailing_double_colon~ set to true if either: =::= is encountered OR there are no path segments and parameter ~kind~ is NOT ~PathKind::Plain~. When UseTreeList makes a recursive call to parse_use_tree_without_kind it sets ~kind~ to ~PathKind::Plain~ as the recursive call explicitly skips any attempts to parse [[#h:96FCF9AD-3B89-451B-B84D-90A7A625B56D][PathKind]] and a NIL PathKind is represented via that value. Parameter ~nested~ is true if UseTreeList makes a recursive call, otherwise default is false. If ~trailing_double_colon~ and ={= then via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] split at =,= repeatedly parsing [[#h:0C07D74C-165A-4128-BF20-EF7E937F7CAA][UseTreeList]] until =}=. Eventually, through UseTreeLists recurive calls, ~trailing_double_colon~ will be true and [[#h:E818DDE4-D341-4AEC-B785-028949A4B4A7][UseTreeAs]] will be parsed. ****** DONE UseTreeList :node: CLOSED: [2025-06-08 Sun 17:35] :PROPERTIES: :CUSTOM_ID: h:0C07D74C-165A-4128-BF20-EF7E937F7CAA :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-08 Sun 17:35] :END: :pgd: + [[#h:19EAD845-0134-41CA-85BA-2E09E10D479B][UseTree]] by (rep0 grp) + =,= + [[#h:19EAD845-0134-41CA-85BA-2E09E10D479B][UseTree]] + (opt) =,= :end: #+name: use_tree_list__path #+begin_src js :rule __use_list_path_prefix :arg $ :ast "UseTreeList -- if path beforehand." seq( optional(field('scope', optional($.__path_no_kind_no_turbofish))), '::', // field('list', $.use_list), field('list', <>), ) #+end_src #+name: use_tree_list__nopath #+begin_src js :rule use_list :arg $ :ast "UseTreeList -- if no path beforehand." seq( '{', // sepBy($.__use_tree_variants, ','), sepBy(<>, ','), optional(','), '}', ) #+end_src Parser (sop): [[file:noir/compiler/noirc_frontend/src/parser/parser/use_tree.rs::let use_trees = self.parse_many(][parse_use_tree_in_list() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/use_tree.rs::fn parse_use_tree_in_list(&mut self)][parse_use_tree_in_list()]] ****** DONE UseTreeAs :leaf: CLOSED: [2025-06-08 Sun 17:35] :PROPERTIES: :CUSTOM_ID: h:E818DDE4-D341-4AEC-B785-028949A4B4A7 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-08 Sun 17:35] :END: :pgd: + (opt grp) + =as= (kw) + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) :end: #+name: use_alias #+begin_src js :arg $ :ast UseTreeAs seq( field('scope', $.__path_no_kind_no_turbofish), 'as', field('alias', $.identifier), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/use_tree.rs::pub(super) fn parse_path_use_tree_end(&mut self, mut prefix: Path, nested: bool)][parse_path_use_tree_end()]] If no path segments it's an error. Otherwise the last path segment constructed by [[#h:7BA3BCB8-65F3-4001-8B02-2F904B014F87][parse_path_after_kind]] has it's ident (which luckily /is/ [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]]) popped, and we eat the keyword =as= followed by an [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]]. Functionally this is just checking =as= followed by an ident. ***** DONE ModOrContract :node: CLOSED: [2025-06-08 Sun 21:02] :PROPERTIES: :CUSTOM_ID: h:9204514E-E5FC-488B-8066-66F0D9AF0C85 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-08 Sun 21:02] :END: :pgd: + (grp) - =mod= (kw) - =contract= (kw) + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) + (grp) - ={= by [[#h:649C4EE8-8F15-4F45-8EA6-3AD48A893930][Module]] by =}= - =;= :end: #+name: mod_or_contract #+begin_src js :rule module_or_contract_item :arg $ :ast ModOrContract seq( optional(<>), choice('mod', 'contract'), // TODO: Discriminate kind into a field? field('name', $.identifier), choice( ';', field('body', $.item_list), ), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::if let Some(is_contract) = self.eat_mod_or_contract() {][parse_mod_or_contract() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/module.rs::pub(super) fn parse_mod_or_contract(][parse_mod_or_contract()]] Only the [[#h:F5A79701-65C9-4FEA-83D8-2413C585A5FA][ItemVisibility]] modifier is applicable, all other [[#h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C][Modifiers]] are parse errors. Eat =mod= or =contract= keywords, ~validate_secondary_attributes~. If the brace group is not hit it's a ~ItemKind::ModuleDecl~ (declaration), otherwise a real submodule. i.e. =mod foo;= is a module declaration and =mod { let x = 123; }= is a real module. ***** SPEC Struct :node: :PROPERTIES: :CUSTOM_ID: h:A53DF633-322A-464B-9947-E56B64A112C7 :END: :LOGBOOK: - State "SPEC" from "DONE" [2025-06-08 Sun 23:43] \\ Need to test generics also. - State "DONE" from "TODO" [2025-06-08 Sun 23:43] :END: :pgd: + =struct= (kw) + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) + (opt) [[#h:BA1422E4-AB97-4099-8346-5845CA9223A1][Generics]] + (opt) =;= (returns via ~empty_struct~) + ={= by (rep0) [[#h:AE5E66EF-9D42-49F2-853D-5C1E7763416E][StructField]] by =}= :end: #+name: struct #+begin_src js :rule struct_item :arg $ :ast Struct seq( optional(<>), 'struct', field('name', $.identifier), // optional($.generics), // TODO: Generics choice( field('body', $.struct_field_list), // TODO: If this is similar to others, e.g. Impl or Enum we can reduce it to one. ';', // Empty struct. ), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::if self.eat_keyword(Keyword::Struct) {][parse_struct() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/structs.rs::pub(crate) fn parse_struct(][parse_struct()]] Types: [[file:noir/compiler/noirc_frontend/src/ast/structure.rs::pub struct NoirStruct {][NoirStruct struct]] Only the [[#h:F5A79701-65C9-4FEA-83D8-2413C585A5FA][ItemVisibility]] modifier is applicable, all other [[#h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C][Modifiers]] are parse errors. ~empty_struct~ ([[file:noir/compiler/noirc_frontend/src/parser/parser/structs.rs::fn empty_struct(][src]]) is a [[file:noir/compiler/noirc_frontend/src/ast/structure.rs::pub struct NoirStruct {][NoirStruct]] with no ~fields~, it may still have a ~name~, associated ~attributes~, ~visibility~, and ~generics~. *************** TODO Struct BNF wrong Semicolon empty struct, and leading visibility modifier. *************** END ****** DONE StructField :node: CLOSED: [2025-06-08 Sun 23:43] :PROPERTIES: :CUSTOM_ID: h:AE5E66EF-9D42-49F2-853D-5C1E7763416E :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-08 Sun 23:43] :END: :pgd: + [[#h:FB5478BF-5E66-4686-931B-733349F83FD8][OuterDocComments]] + (opt) [[#h:F5A79701-65C9-4FEA-83D8-2413C585A5FA][ItemVisibility]] + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) + =:= + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] :end: #+name: struct_field #+begin_src js :rule struct_field_item :arg $ :ast StructField seq( optional(<>), field('name', $.identifier), ':', field('type', $._type), ) #+end_src #+name: struct_field_list #+begin_src js :arg $ seq( '{', sepBy(<>, ','), optional(','), '}', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/structs.rs::let fields = self.parse_many(][parse_struct_field() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/structs.rs::fn parse_struct_field(&mut self)][parse_struct_field()]] Types: [[file:noir/compiler/noirc_frontend/src/ast/structure.rs::pub struct StructField {][StructField struct]] Since [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] is used, and our callsite does not check, a trailing comma (the separator in use) is accepted. *************** TODO If this is general enough and in-use elsewhere like Impl or Enum then reduce it to 1. *************** END *************** TODO BNF for StructField is wrong Parses item visibility. Update at remote after grammar is done. *************** END ***** BLOCK Implementation :node:cluster: :PROPERTIES: :CUSTOM_ID: h:954A182F-703F-48FC-85D3-37C11EC959A3 :END: :LOGBOOK: - State "BLOCK" from "TODO" [2025-06-10 Tue 16:59] \\ One child depends on Function and Function is also a top-level ItemKind so BLOCK until Function is done. :END: :pgd: - [[#h:2C79F6AF-9B0D-4246-809D-D5D91C742F09][TypeImpl]] - [[#h:EBF1C149-1814-4314-AAFB-C5CF198DA61D][TraitImpl]] :end: #+name: impl #+begin_src js :rule impl_item :arg $ seq( 'impl', // TODO: Generics // TODO: Path // TODO: Choice between TypeImpl or TraitImpl // $.trait_impl, <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::if self.eat_keyword(Keyword::Impl) {][parse_impl() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::pub(crate) fn parse_impl(&mut self)][parse_impl()]] Types: [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::pub(crate) enum Impl {][Impl enum]], [[file:noir/compiler/noirc_frontend/src/ast/traits.rs::pub struct TypeImpl {][TypeImpl struct]], [[file:noir/compiler/noirc_frontend/src/ast/traits.rs::pub struct NoirTraitImpl {][NoirTraitImpl struct]] Only the [[#h:F5A79701-65C9-4FEA-83D8-2413C585A5FA][ItemVisibility]] modifier is applicable, all other [[#h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C][Modifiers]] are parse errors. =impl= eaten before parse_impl called, within ~parse_impl~: ~parse_generics~, ~parse_type_or_error~ then eat =for= (kw). If eaten try and parse TraitImpl, otherwise TypeImpl. For immediate children {Type,Trait}Impl their Generics are parsed here as is Path and GenericTypeArgs but the latter two indirectly via Type. Return type ~UnresolvedTypeData::Named~ from ~parse_type_or_error~ is unwrapped if =for= is eaten. That enumerant captures Path and GenericTypeArgs ([[file:noir/compiler/noirc_frontend/src/ast/mod.rs::Named(Path, GenericTypeArgs, /*is_synthesized*/ bool),][src]]). ****** TOIMPL TypeImpl :node: :PROPERTIES: :CUSTOM_ID: h:2C79F6AF-9B0D-4246-809D-D5D91C742F09 :END: :LOGBOOK: - State "TOIMPL" from "TODO" [2025-06-09 Mon 14:34] \\ Simple wrapper function. :END: :pgd: + =impl= (kw) by [[#h:BA1422E4-AB97-4099-8346-5845CA9223A1][Generics]] by [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] + (opt) [[#h:ED279BDF-F033-4C47-9105-6AC549CE7C31][WhereClause]] + [[#h:4E657C7B-45FD-45D0-84B2-DB55C1D09192][TypeImplBody]] :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_type_impl(][parse_type_impl()]] *************** TODO BNF error, whereclause is missing? *************** END ******* TOIMPL TypeImplBody :node: :PROPERTIES: :CUSTOM_ID: h:4E657C7B-45FD-45D0-84B2-DB55C1D09192 :END: :LOGBOOK: - State "TOIMPL" from "TODO" [2025-06-09 Mon 14:41] :END: :pgd: + ={= by (rep0) [[#h:268CE151-7A1D-48ED-A6ED-40219D809438][TypeImplItem]] by =}= :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_type_impl_body(&mut self)][parse_type_impl_body()]] Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] inline split without separator until =}= calls [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_type_impl_method(&mut self)][parse_type_impl_method()]] which is a parser utility function for error recovery, there a lambda defines the parsing for each TypeImplItem. ******* TODO TypeImplItem :node: :PROPERTIES: :CUSTOM_ID: h:268CE151-7A1D-48ED-A6ED-40219D809438 :END: :pgd: + [[#h:FB5478BF-5E66-4686-931B-733349F83FD8][OuterDocComments]] by [[id:DA6150E7-E986-4749-A481-A95FF1368B74][Attributes]] by [[#h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C][Modifiers]] by [[#h:B3C4609F-307A-42A1-B420-DBBAB6CDE6E5][Function]] :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_type_impl_method(&mut self)][parse_type_impl_method()]] Parser is a lambda on call to ~parse_item_in_list~. Modifiers parsed here allow all except mutable ([[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::let modifiers = parser.parse_modifiers(][src]]). JORDAN: This looks done, need to do ts grammar for this node up. Calls to function ends this, the parsed attributes are associated with the function though right? ****** TODO TraitImpl :node: :PROPERTIES: :CUSTOM_ID: h:EBF1C149-1814-4314-AAFB-C5CF198DA61D :END: :pgd: + =impl= (kw) by [[#h:BA1422E4-AB97-4099-8346-5845CA9223A1][Generics]] by [[#h:07167116-EAE4-475B-8C87-DE9075BAF88D][Path]] by [[#h:3267D7A4-7AA4-49FB-91FA-A9601BC6868A][GenericTypeArgs]] by =for= (kw) by [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] + (opt) [[#h:ED279BDF-F033-4C47-9105-6AC549CE7C31][WhereClause]] + [[#h:AE12BD3E-D350-4D0C-88E7-2FC471E9FB9C][TraitImplBody]] :end: #+name: trait_impl #+begin_src js :arg $ seq( // TODO: Path <>, 'for', $._type, // optional($.where_clause), // Temp commented for now due to prec error. ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::return Impl::TraitImpl(self.parse_trait_impl(][parse_trait_impl() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_trait_impl(][parse_trait_impl()]] Parent parses Generics, Path, and GeenricTypeArgs before we're reached so here we parse from Type. ?? -> indeed in ~parse_trait_impl~ a Type is parsed first followed by TraitImplBody. *************** TODO BNF mistake in TraitImpl? Is the where clause meant to be absent there? *************** END ******* TODO TraitImplBody :node: :PROPERTIES: :CUSTOM_ID: h:AE12BD3E-D350-4D0C-88E7-2FC471E9FB9C :END: :pgd: + ={= by (rep0) [[#h:E070D1E3-2377-4041-96A0-C4DA104E9A9C][TraitImplItem]] by =}= :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_trait_impl_body(&mut self)][parse_trait_impl_body()]] Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] inline split without separator until =}= calls [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_trait_impl_item(&mut self)][parse_trait_impl_item()]] which is a parser utility function for error recovery, calls to [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_trait_impl_item_kind(&mut self)][parse_trait_impl_item_kind()]] which /is/ [[#h:E070D1E3-2377-4041-96A0-C4DA104E9A9C][TraitImplItem]]. ******* TOIMPL TraitImplItem :node: :PROPERTIES: :CUSTOM_ID: h:E070D1E3-2377-4041-96A0-C4DA104E9A9C :END: :LOGBOOK: - State "TOIMPL" from "SPEC" [2025-06-09 Mon 01:48] - State "SPEC" from "TODO" [2025-06-09 Mon 01:48] :END: :pgd: - [[#h:D3AE715D-AC3E-494A-9E32-0BAD666C2302][TraitImplType]] - [[#h:400F4495-94E9-4549-9E85-9FECC5F69E7A][TraitImplConstant]] - [[#h:668ACDEF-C6C3-46B2-AF64-3907253E7EA6][TraitImplFunction]] :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_trait_impl_item_kind(&mut self)][parse_trait_impl_item_kind()]] Returns: [[file:noir/compiler/noirc_frontend/src/ast/traits.rs::pub enum TraitImplItemKind {][TraitImplItemKind enum]] Simple wrapper which calls parsers for pgd types in order. ******* TODO TraitImplType :node: :PROPERTIES: :CUSTOM_ID: h:D3AE715D-AC3E-494A-9E32-0BAD666C2302 :END: :pgd: + =type= (kw) + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) + (opt grp) =:= by [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] + =;= :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/impls.rs::fn parse_trait_impl_type(&mut self)][parse_trait_impl_type()]] ******* TODO TraitImplConstant :node: :PROPERTIES: :CUSTOM_ID: h:400F4495-94E9-4549-9E85-9FECC5F69E7A :END: :pgd: + =let= (kw) + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) + [[#h:644406F7-BDEE-414D-85D6-0B016CA0C929][OptionalTypeAnnotation]] + =;= :end: ******* TODO OptionalTypeAnnotation :node: :PROPERTIES: :CUSTOM_ID: h:644406F7-BDEE-414D-85D6-0B016CA0C929 :END: *************** TODO Put this node elsewhere, its only here for now Cos I am lazy while going through Impl for ts grammar *************** END ******* TODO TraitImplFunction :node: :PROPERTIES: :CUSTOM_ID: h:668ACDEF-C6C3-46B2-AF64-3907253E7EA6 :END: :pgd: + [[id:DA6150E7-E986-4749-A481-A95FF1368B74][Attributes]] + [[#h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C][Modifiers]] + [[#h:B3C4609F-307A-42A1-B420-DBBAB6CDE6E5][Function]] :end: ***** TODO Trait Foo Only the [[#h:F5A79701-65C9-4FEA-83D8-2413C585A5FA][ItemVisibility]] modifier is applicable, all other [[#h:2A9D09A5-2E52-4B8C-ABA1-0B3B953F093C][Modifiers]] are parse errors. ***** TODO Global :cluster:node: :PROPERTIES: :CUSTOM_ID: h:C21A19F9-DC0C-4F17-9739-B92412D0C4FF :END: :pgd: + =global= (kw) by [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) by OptionalTypeAnnotation (TODO) + === by [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] by =;= :end: #+name: global #+begin_src js :rule global_item :arg $ :ast Global seq( 'global', field('name', $.identifier), // TODO: OptionalTypeAnnotation. '=', <>, // prec.left(1, $._expression), ';', ) #+end_src // TODO: Put this elsewhere. ExpressionKind::Literal see ast/expression.rs #+name: literal #+begin_src js :rule __literal :arg $ :ast Literal choice( <>, <>, <>, <>, <>, // $.quote_expression, // TODO: Broken for now. <>, <>, <>, ) #+end_src Parser (sop): [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::if self.eat_keyword(Keyword::Global) {][parse_global() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/global.rs::pub(crate) fn parse_global(][parse_global()]] 2025/06/12: document (as appropriate) ~parse_global~. ***** TOIMPL Type :node:cluster: :PROPERTIES: :CUSTOM_ID: h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581 :END: :LOGBOOK: - State "TOIMPL" from "TODO" [2024-11-16 Sat 18:26] :END: :pgd: - [[#h:C23E374A-42EF-467D-AE3D-548D880712D8][PrimitiveType]] - [[#h:39A2690C-319F-4F34-A139-8549D70FCF26][ParenthesesType]] - [[#h:FAB3845D-97FD-454B-B8C8-09FEEB41FC3D][ArrayOrSliceType]] - [[#h:C1EF6337-B3F1-44D9-B5E6-FF5A0215FCC5][MutableReferenceType]] - [[#h:96043AA8-9EC3-4E80-AA5D-CBFFE60A9072][FunctionType]] - [[#h:08118263-BDC8-4589-8EF9-1F0490B62F34][TraitAsType]] - [[#h:5B4FDF39-09B2-4C07-A2B5-3A3D6BEDD4B7][AsTraitPathType]] - [[#h:F2E3D7E1-0978-44DF-A49E-EBE348F9D973][UnresolvedNamedType]] :end: #+name: type #+begin_src js :rule _type :arg $ choice( <>, $._parentheses_type, // $.array_or_slice_type, // $.mutable_reference_type, // $.function_type, // TODO: TraitAsType, AsTraitPathType, UnresolvedNamedType ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_type(&mut self)][parse_type()]] TODO: Look at enum ~UnresolvedTypeData~, read the doc string and look at it's elements. Good stuff. #+transclude: [[file:noir/compiler/noirc_frontend/src/ast/mod.rs][foobar]] :lines 104-106 :src foo TODO: Path from lexer.rs ~next_token()~ to the point where it checks for keywords. TODO: Put lookup_keyword under it's own heading and transclude the contents verbatim When lexing [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::fn lookup_keyword(word: &str)][lookup_keyword()]] determines keyword tokens ~Keyword~ which are later parsed: :callstack: - [-] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_unresolved_type_data(&mut self)][parse_unresolved_type_data()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_str_type(&mut self)][parse_str_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_fmtstr_type(&mut self)][parse_fmtstr_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_comptime_type(&mut self)][parse_comptime_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_resolved_type(&mut self)][parse_resolved_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_interned_type(&mut self)][parse_interned_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_parentheses_type(&mut self)][parse_parentheses_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_array_or_slice_type(&mut self)][parse_array_or_slice_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parses_mutable_reference_type(&mut self)][parses_mutable_reference_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_function_type(&mut self)][parse_function_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_trait_as_type(&mut self)][parse_trait_as_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_as_trait_path_type(&mut self)][parse_as_trait_path_type()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::fn parse_path_no_turbofish(&mut self)][parse_path_no_turbofish()]] - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::fn parse_generic_type_args(&mut self)][parse_generic_type_args()]] :end: ****** BLOCK PrimitiveType :node: :PROPERTIES: :CUSTOM_ID: h:C23E374A-42EF-467D-AE3D-548D880712D8 :END: :LOGBOOK: - State "BLOCK" from "TODO" [2024-11-15 Fri 19:40] \\ Explored the parsing function for all its subtypes, but there are some non-trivial recursive dependencies (going back to Type) and also some quoted and interned stuff so blocked for now. :END: :pgd: - [[#h:B5115CEE-F6AE-4BB2-82EE-13F65B7EDC43][FieldType]] - [[#h:8343D34F-D36E-4E46-A343-A9A99A1F3200][IntegerType]] - [[#h:57CAEB11-8421-4186-A495-818CC08D5810][BoolType]] - [[#h:C405DA7F-97A3-4DCF-8946-7480E43D2CC2][StringType]] - [[#h:12C0CC82-5210-45ED-97F4-0266E1CAA6F1][FormatStringType]] - [[#h:98FC5657-8034-42F7-A263-3172EFEEEB23][ComptimeType]] (TODO: Not viable in AST, see heading for more ignoring for now but leaving here) - [[#h:87984AE0-613D-4D9A-A64D-D3FD8BD51C98][ResolvedType]] (TODO: Also ignore, similar reasons for now) - [[#h:21789349-305A-46D0-8D87-D7B8647482CF][InternedType]] (TODO: Also ignore, similar reasons for now) :end: #+name: primitive_type #+begin_src js :arg $ choice( <>, <>, <>, <>, <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_primitive_type(&mut self)][parse_primitive_type()]] *************** TODO Convention is to have all primitive types be a single node, do we keep that? Double check other grammars none appear to go deeper on primitive types (intentionally). So for example =i32= or =Field= is an IntegerType and FieldType respectively but none actually make a CST node called that and instead both would be PrimitiveType. I suppose you can get the match region when tagging to determine what it is, or there's just too many nodes for primitives since convention shows people _do_ have CST nodes for arrays, unit types, etc. I'll follow convention for now and make all the primitive types anonymous nodes by default. *************** END ******* SPEC FieldType :leaf: :PROPERTIES: :CUSTOM_ID: h:B5115CEE-F6AE-4BB2-82EE-13F65B7EDC43 :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 22:10] - State "TOIMPL" from "TODO" [2024-11-03 Sun 12:24] :END: :pgd: + =Field= (kw) :end: #+name: field_type #+begin_src js :rule __field_type 'Field' #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_field_type(&mut self)][parse_field_type()]] ******* SPEC IntegerType :leaf: :PROPERTIES: :CUSTOM_ID: h:8343D34F-D36E-4E46-A343-A9A99A1F3200 :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 22:10] - State "TOIMPL" from "TODO" [2024-11-03 Sun 14:50] :END: :pgd: + =i= OR =u= + =1= OR =8= OR =16= OR =32= OR =64= :end: #+name: int_type #+begin_src js :rule __integer_type choice(...INTEGER_TYPES) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_int_type(&mut self)][parse_int_type()]] Both signed and unsigned: 1, 8, 16, 32, and 64 bits. #+transclude: [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::61][IntegerBitSize::allowed_sizes()]] :lines 61-65 :src rust *************** TODO Is there a bug in this version of Noir that doesn't allow 16-bit integers? See the above transclude missing ~Self::Sixteen~. *************** END :callstack: - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::fn eat_int_type(&mut self)][eat_int_type()]] - nb :: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::enum IntType][Token::IntType]] from [[#h:B7763FFE-9685-45F5-A414-66F9E47F3E1D][Lexing]]. - [X] [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::fn from_int_token(][UnresolvedTypeData::from_int_token()]] - [X] [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::fn try_from(value: u32)][IntegerBitSize::try_from()]] :end: 1. If [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::fn from_int_token(][UnresolvedTypeData::from_int_token()]]'s call to [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::fn try_from(value: u32)][IntegerBitSize::try_from()]] succeeds an ~UnresolvedTypeData::Integer~ is returned. 2. [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::fn try_from(value: u32)][IntegerBitSize::try_from()]] validates given numeric bit-size component and returns matching ~IntegerBitSize~, otherwise returning invalid bit-size parse error. ******** Lexing :PROPERTIES: :CUSTOM_ID: h:B7763FFE-9685-45F5-A414-66F9E47F3E1D :END: :callstack: - [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn next_token(&mut self)][Lexer::next_token()]] - [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_alpha_numeric(&mut self, initial_char: char)][Lexer::eat_alpha_numeric()]] - [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_word(&mut self, initial_char: char)][Lexer::eat_word()]] - [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn lookup_word_token(][Lexer::lookup_word_token()]] :end: Parser: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::fn lookup_int_type(word: &str)][IntType::lookup_int_type()]] 1. Signed or unsigned if ~word~ starts with =i= or =u= respectively. 2. Remaining string ~word~ contents attempt parse into bit-size 32-bit integer. 3. [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::enum IntType][Token::IntType]] created with signedness and bit-size value. ******* SPEC BoolType :leaf: :PROPERTIES: :CUSTOM_ID: h:57CAEB11-8421-4186-A495-818CC08D5810 :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 22:13] - State "TOIMPL" from "TODO" [2024-11-03 Sun 12:21] :END: :pgd: + =bool= (kw) :end: #+name: bool_type #+begin_src js :rule __bool_type 'bool' #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_bool_type(&mut self)][parse_bool_type()]] Parses the literal /keyword/ =bool= *and not* literal words =true= or =false=. ******* BLOCK StringType :node: :PROPERTIES: :CUSTOM_ID: h:C405DA7F-97A3-4DCF-8946-7480E43D2CC2 :END: :LOGBOOK: - State "BLOCK" from "TODO" [2024-11-16 Sat 18:26] \\ TypeExpression completion. :END: :pgd: + =str= (kw) + =<= + [[#h:0DEF3192-4840-41B3-A941-714798677092][TypeExpression]] + =>= :end: #+name: str_type #+begin_src js :rule __string_type :arg $ seq( 'str', '<', // TODO: TypeExpression goes here. '>', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_str_type(&mut self)][parse_str_type()]] *************** TODO How does the first check on eat_less in parse_str_type work? What's going on here specifically when it returns Some(UnresolvedTypeExpression)? Is that important later on? Check `str` syntax with concrete tests in Noir, like is str allowed or does it always have to be str<123>. It looks like it _is_ a parser error so..? *************** END ******* SPEC FormatStringType :node: :PROPERTIES: :CUSTOM_ID: h:12C0CC82-5210-45ED-97F4-0266E1CAA6F1 :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 22:16] - State "TOIMPL" from "TODO" [2024-11-16 Sat 18:24] :END: :pgd: + =fmtstr= (kw) :end: #+name: fmt_str_type #+begin_src js :rule __format_string_type 'fmtstr' #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_fmtstr_type(&mut self)][parse_fmtstr_type()]] Return ~AST::UnresolvedTypeData::FormatString~. *************** TODO Document the parser for this (in this FormatStringType heading) Missing docs, on paper I note that FmtStrType can be a TypeExpression or a Type (both recursively) but I don't have those notes right here in the org file. Looking at the linked parser I see the logic, so note that here under this heading since that informs the grammar construction and we want this info in the actual org file. *************** END ******* TODO ComptimeType :PROPERTIES: :CUSTOM_ID: h:98FC5657-8034-42F7-A263-3172EFEEEB23 :END: :LOGBOOK: - State "TODO" from [2025-06-08 Sun 23:36] \\ Found snippets of these, they can exist literally in source. - State "TOIMPL" from "TODO" [2024-11-15 Fri 19:29] :END: :pgd: - =Expr= (kw) - =Quoted= (kw) - =TopLevelItem= (kw) - =Type= (kw) - =TypedExpr= (kw) - =StructDefinition= (kw) - =TraitConstraint= (kw) - =TraitDefinition= (kw) - =TraitImpl= (kw) - =UnresolvedType= (kw) - =FunctionDefinition= (kw) - =Module= (kw) - =CtString= (kw) :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_comptime_type(&mut self)][parse_comptime_type()]] All ComptimeTypes returned as ~AST::UnresolvedTypeData::Quoted(X)~ where ~X~ is [[file:noir/compiler/noirc_frontend/src/hir_def/types.rs::pub enum QuotedType {][QuotedType]]. #+transclude: [[file:noir/compiler/noirc_frontend/src/hir_def/types.rs::282][QuotedType]] :lines 282-296 :src rust *************** TODO This feels strage, are those literal keywords allowed in source? This feels like stuff a preprocessing step would inline or something. If one can literally write these keywords and them be valid then sure. 2025/05/24: this is metaprogramming quoted type HIR(?) stuff, these cannot appear as literal keywords I'm 99% sure so not adding them for now. *************** END *************** TODO Check out hir_def/types.rs enum Type, good docs. *************** END ******* BLOCK ResolvedType :PROPERTIES: :CUSTOM_ID: h:87984AE0-613D-4D9A-A64D-D3FD8BD51C98 :END: :LOGBOOK: - State "BLOCK" from "TODO" [2024-11-16 Sat 18:25] \\ Clarity on QuotedType stuff. :END: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_resolved_type(&mut self)][parse_resolved_type()]] *************** TODO Non-concrete token kinds lookup, so get to this later. i.e. no literal string keyword that lookup_keyword matches. *************** END ******* BLOCK InternedType :PROPERTIES: :CUSTOM_ID: h:21789349-305A-46D0-8D87-D7B8647482CF :END: :LOGBOOK: - State "BLOCK" from "TODO" [2024-11-16 Sat 18:25] \\ Clarity on InternedType stuff. :END: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::pub(super) fn parse_interned_type(&mut self)][parse_interned_type()]] *************** TODO Non-concrete token kinds lookup, so get to this later. i.e. no literal string keyword that lookup_keyword matches. *************** END ****** SPEC ParenthesesType :node: :PROPERTIES: :CUSTOM_ID: h:39A2690C-319F-4F34-A139-8549D70FCF26 :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-05-24 Sat 22:48] \\ Type isn't really a blocker, as Type becomes more complete so will the types which recursive with it. In this case only TupleType does. - State "BLOCK" from "TODO" [2024-11-16 Sat 17:09] \\ Type completion. :END: :pgd: - [[#h:9653A5E5-2857-4FB4-8698-5D9F6F0E8755][UnitType]] - [[#h:CF9505CB-8110-4FC2-8A6B-5D88A7EB1D06][TupleType]] :end: #+name: parentheses_type #+begin_src js :rule _parentheses_type :arg $ choice( <>, <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_parentheses_type(&mut self)][parse_parentheses_type()]] ******* SPEC UnitType :leaf: :PROPERTIES: :CUSTOM_ID: h:9653A5E5-2857-4FB4-8698-5D9F6F0E8755 :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-20 Tue 17:11] - State "TOIMPL" from "TODO" [2024-11-15 Fri 20:24] :END: :pgd: + =(= by =)= :end: #+name: unit_type #+begin_src js seq('(', ')') #+end_src Parser: /in slice of parent/. ******* SPEC TupleType :node: :PROPERTIES: :CUSTOM_ID: h:CF9505CB-8110-4FC2-8A6B-5D88A7EB1D06 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-05-24 Sat 22:47] \\ As Type gets more complete so will this (since it's recursive). :END: :pgd: + =(= + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] + (rep0 grp) =,= by [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] + (opt) =,= + =)= :end: #+name: tuple_type #+begin_src js :arg $ seq( '(', sepBy1(<>, ','), optional(','), ')', ) #+end_src Parser: /in slice of parent/. #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::387][parse_many_return_trailing_separator_if_any()]] :lines 387-397 :src rust If only one Type and no trailing comma return ~AST::UnresolvedTypeData::Parenthesized~, else return ~AST::UnresolvedTypeData::Tuple~. *************** TODO If 1 element in list and no trailing comma Parnthesized, else Tuple Is there a meaningful difference here for tree-sitter purposes? *************** END ****** SPEC ArrayOrSliceType :node: :PROPERTIES: :CUSTOM_ID: h:FAB3845D-97FD-454B-B8C8-09FEEB41FC3D :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-05-24 Sat 23:05] \\ More complete as Type/TypeExpression increase in completeness. - State "BLOCK" from "TODO" [2024-11-16 Sat 17:09] \\ Type and TypeExpression completion. :END: :pgd: + =[= + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] by: - =]= returns ~AST::UnresolvedTypeData::Slice~ - =;= by [[#h:0DEF3192-4840-41B3-A941-714798677092][TypeExpression]] by =]= returns ~AST::UnresolvedTypeData::Array~ :end: #+begin_src js :treesit t array_or_slice_type: ($) => seq( '[', $._type, optional(seq( ';', $.type_expr, // TODO: this rule )), ']', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_array_or_slice_type(&mut self)][parse_array_or_slice_type()]] *************** TODO What about slice literal syntax? What about the &[0; 2] syntax for slice literals? This node doesn't appear to do any ampersand parsing? Check higher up the call chain though since there's A LOT of nesting and what not going on currently. *************** END *************** TODO Not going to differentiate between Array or Slice, also technically the first Type calls up to parse_type_or_error does it make a diff from ts pov? *************** END ****** SPEC MutableReferenceType :node: :PROPERTIES: :CUSTOM_ID: h:C1EF6337-B3F1-44D9-B5E6-FF5A0215FCC5 :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-05-24 Sat 23:09] \\ More complete alongside Type. - State "BLOCK" from "TODO" [2024-11-16 Sat 17:09] \\ Block on Type completion. :END: :pgd: + =&= + =mut= (kw) + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] :end: #+begin_src js :treesit t mutable_reference_type: ($) => seq( '&', 'mut', $._type, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parses_mutable_reference_type(&mut self)][parse_mutable_reference_type()]] Return: ~AST::UnresolvedTypeData::MutableReference~. ****** SPEC FunctionType :node: :PROPERTIES: :CUSTOM_ID: h:96043AA8-9EC3-4E80-AA5D-CBFFE60A9072 :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-05-24 Sat 23:13] \\ More complete alongside Type. - State "BLOCK" from "TODO" [2024-11-16 Sat 17:37] \\ Type completion. :END: :pgd: + (opt) =unconstrained= (kw) + =fn= (kw) + (opt) [[#h:4E68CB10-4122-4C09-B99F-DE7F57651E02][CaptureEnvironment]] + [[#h:FC1E33B7-033B-4F6F-9EFD-2CDC784E572C][Parameter]] + =->= + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] :end: #+begin_src js :treesit t function_type: ($) => seq( 'unconstrained', 'fn', optional($.capture_environment), $.parameter_list, '->', $._type, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_function_type(&mut self)][parse_function_type()]] Return: ~AST::UnresolvedTypeData::Function~. *************** TODO So this is specifically NOT a function DECLARATION? It's either an inline function e.g. a lambda, or calling another pre-declared function? Because there's already a function for parsing a function declaration in the compiler frontend as far as I can see and that function does not support environment capture. It's been a while but I believe I have some environment capture stuff in Tikan from old noir, see how that's used and if that pattern is still valid in current Noir (as well as consulting the compiler tests) to determine what is valid now if there's still uncertainty once all frontend paths are documented. *************** END ******* SPEC CaptureEnvironment :node: :PROPERTIES: :CUSTOM_ID: h:4E68CB10-4122-4C09-B99F-DE7F57651E02 :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-05-24 Sat 23:22] \\ More complete alongside Type. - State "BLOCK" from "TODO" [2024-11-16 Sat 17:34] \\ Type completion. :END: :pgd: + =[= + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] + =]= :end: #+begin_src js :treesit t capture_environment: ($) => seq( '[', $._type, ']', ) #+end_src Function capture environment syntax, e.g. =fn foo[Env]()= where =[Env]= is the environment specifier valid for the function to be called within. Parser: /within slice of parent/ #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::252][FunctionType CaptureEnvironment]] :lines 252-258 :src rust ******* SPEC Parameter :node: :PROPERTIES: :CUSTOM_ID: h:FC1E33B7-033B-4F6F-9EFD-2CDC784E572C :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-05-24 Sat 23:22] \\ More complete alongside Type. - State "BLOCK" from "TODO" [2024-11-16 Sat 17:34] \\ Type completion. :END: :pgd: + =(= + (rep0 grp) [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] by =,= + =)= :end: #+begin_src js :treesit t parameter_list: ($) => seq( '(', sepBy($._type, ','), ')', ) #+end_src Parser: /within slice of parent/ and [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_parameter(&mut self)][parse_parameter()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::271][parse_many()]] :lines 271-275 :src rust *************** TODO Are parameters here required? Surely not but until all paths explored they might be? Could easily validate with a concrete syntax test however. The documentation for (rep0 grp) here differs from other lsits because in those other cases at least one element was required before the optional repeating, as this TODO states unsure if the "at least one" is required here. No trailing comma here either right? *************** END *************** TODO Move this node parameter_list elsewhere? It feels pretty generic, so we should put it somewhere in grammar.js that reflects that (as appropriate). *************** END ****** BLOCK TraitAsType :node: :PROPERTIES: :CUSTOM_ID: h:08118263-BDC8-4589-8EF9-1F0490B62F34 :END: :LOGBOOK: - State "BLOCK" from "TODO" [2024-11-16 Sat 17:48] \\ PathNoTurbofish and GenericTypeArgs completion. :END: :pgd: + =impl= (kw). + [[#h:A051D0D5-7007-4DF8-83B7-FB4EFF9C383E][PathNoTurbofish]]. + [[#h:3267D7A4-7AA4-49FB-91FA-A9601BC6868A][GenericTypeArgs]]. :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_trait_as_type(&mut self)][parse_trait_as_type()]] Return: ~AST::UnresolvedTypeData::TraitAsType~. *************** TODO Rename this to just TraitType if that is unused elsewhere in the (relevant) compiler frontend section? *************** END *************** TODO Path is implied optional (as noted elsewhere), is PathNoTurbofish also implied optional? Because here its required that PathNoTurbofish is Some and _not_ None (which IIRC is how/why Path is implied optional elsewhere). *************** END ****** BLOCK AsTraitPathType :node: :PROPERTIES: :CUSTOM_ID: h:5B4FDF39-09B2-4C07-A2B5-3A3D6BEDD4B7 :END: :LOGBOOK: - State "BLOCK" from "TODO" [2024-11-16 Sat 18:10] \\ AsTraitPath completion. :END: :pgd: + [[#h:348C4ABC-3FC0-46EC-95A0-7F4B238BB86A][AsTraitPath]]. :end: This just wraps AsTraitPath. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_as_trait_path_type(&mut self)][parse_as_trait_path_type()]] Return: ~AST::UnresolvedTypeData::AsTraitPath~. ****** BLOCK UnresolvedNamedType :node: :PROPERTIES: :CUSTOM_ID: h:F2E3D7E1-0978-44DF-A49E-EBE348F9D973 :END: :LOGBOOK: - State "BLOCK" from "TODO" [2024-11-16 Sat 18:22] \\ PathNoTurbofish completion. :END: :pgd: + [[#h:A051D0D5-7007-4DF8-83B7-FB4EFF9C383E][PathNoTurbofish]]. + (opt) [[#h:3267D7A4-7AA4-49FB-91FA-A9601BC6868A][GenericTypeArgs]]. :end: Parser: /within slice of parent/ #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::58][parent parser slice]] :lines 58-61 :src rust Return: ~AST::UnresolvedTypeData::Named~. #+transclude: [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::121][UnresolvedTypeData::Named doc]] :lines 121-122 :src rust *************** TODO Unsure of the exact syntax here, should be a path (no turbofish) with generics after it per the method calls. Really deep in syntax spaghetti right now, I expect this will be clear when finally some tests are written and what not. Also I named this node since it doesn't appear to canonically have one. I also ASSUME the generic type args are optional (test concretely). *************** END ***** TODO TypeOrTypeExpression :node: :PROPERTIES: :CUSTOM_ID: h:A32A351C-092B-42F1-AB03-DE49862B35D4 :END: :pgd: - [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] - [[#h:0DEF3192-4840-41B3-A941-714798677092][TypeExpression]] :end: Compiler: [[file:noir/compiler/noirc_frontend/src/parser/parser/type_expression.rs::196][parse_type_or_type_expression()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/type_expression.rs::195][TypeOrTypeExpression doc]] :lines 195-195 :src rust Parse logic when entered here attempts to parse any valid non-literal type, before finally calling ~parse_type()~ which /is/ [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]]. - [ ] ~parse_add_or_subtract_type_or_type_expression()~. - [ ] ~parse_multiply_or_divide_or_modulo_type_or_type_expression()~. - [ ] ~parse_term_type_or_type_expression()~. - [ ] ~parse_atom_type_or_type_expression()~. - [ ] ~parse_parenthesized_type_or_type_expression()~. - [ ] ~parse_type()~. ***** DONE TypeExpression :node: CLOSED: [2025-06-22 Sun 02:13] :PROPERTIES: :CUSTOM_ID: h:0DEF3192-4840-41B3-A941-714798677092 :END: :LOGBOOK: - State "DONE" from "TOIMPL" [2025-06-22 Sun 02:13] - State "TOIMPL" from "TODO" [2024-11-16 Sat 20:20] :END: Using 'expr' in-place of 'expression' so-as-to not conflate with _real_ expressions. #+name: type_expr #+begin_src js :rule _type_expr :arg $ :ast TypeExpression choice( alias(<>, <>), // TODO: Replace literal $.unary_expression with noweb ref function alias(<>, $.unary_expression), <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/type_expression.rs::pub(crate) fn parse_type_expression(][parse_type_expression()]] Type: [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::pub enum UnresolvedTypeExpression {][UnresolvedTypeExpression enum]] Parts of regular [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] reused since TypeExpression is a limited subset. No distinction made in CST. A special kind of type intended for narrow usage. Limited to constant integers, variables, and basic numeric binary operators; this heading and it's children define a special type that is allowed in the length position of an array (and some other limited places). Further context (code has diverged however) on implementing PR: [[https://github.com/noir-lang/noir/pull/620/commits/adb969d178fd9f50be91229505138e53bdc4a6f8#diff-ad71a918cf63410fc5be767b6f3ad78a213b22cff60ddd0549c9f5e083a5d6c2][found here]]. ****** Infix The entire call-tree from AddOrSubtractTypeExpression's parser [[file:noir/compiler/noirc_frontend/src/parser/parser/type_expression.rs::fn parse_add_or_subtract_type_expression(&mut self)][parse_add_or_subtract_type_expression()]] forms the same (subset) binary expressions as in [[#h:ACCBF119-4052-4C4E-87CE-F7B233FF4AA1][Expression Infix]] thus similarly to there, it has been flattened here. #+name: binary_type_expr #+begin_src js :rule __binary_type_expr :arg $ :ast "AddOrSubtractTypeExpression -- Entire nested hierarchy flattened and renamed." { const t = [ // Highest to lowest. [PRECEDENCE.multiplicitive, choice('*', '/', '%',)], [PRECEDENCE.additive, choice('+', '-')], ] return choice(...t.map(([p, o]) => prec.left(p, seq( field('left', <>), field('operator', o), field('right', <>), )))) } #+end_src ****** DONE TermTypeExpression :node: CLOSED: [2025-06-22 Sun 02:12] :PROPERTIES: :CUSTOM_ID: h:414EDA73-C80F-4081-A2FC-D4C7DFEDC964 :END: :LOGBOOK: - State "DONE" from "TOIMPL" [2025-06-22 Sun 02:12] :END: #+name: unary_type_expr #+begin_src js :rule __unary_type_expr :arg $ :ast TermTypeExpression prec(PRECEDENCE.unary, seq('-', <>)) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/type_expression.rs::fn parse_term_type_expression(&mut self)][parse_term_type_expression()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/type_expression.rs::111][TermTypeExpression doc]] :lines 111-113 :src fundamental This parser is effectively defining =-= as the single valid unary operator and pretends it is a binary one by setting the =lhs= of that to 0. This is probably for some internal compiler reason, I'll treat this as the unary expression is is for the purpose of the CST. The parser continually eats =-= forming that "binary" (really, unary) expression until no =-= remain after which AtomTypeExpression is parsed which contains top-level recursion via ParenthesisedTypeExpression. *************** TODO In-code docs for TermTypeExpression minus missing a closing apostrophe Teeny tiny change, fix if not already done as it seems Noir is now at 0.38.0 so some updating will be required anyway. *************** END ****** SPEC AtomTypeExpression :node: :PROPERTIES: :CUSTOM_ID: h:4E7B0693-6BFF-4B90-A0FB-6BD577A41989 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-22 Sun 02:12] \\ Still needs Path completed for full test coverage. - State "TODO" from [2025-06-21 Sat 19:07] :END: #+name: atom_type_expr #+begin_src js :rule __atom_type_expr :arg $ :ast AtomTypeExpression choice( <>, // Inlined Noirc: ConstantTypeExpression. <>, // Inlined Noirc: VariableTypeExpression. // TODO: Replace hardcoded rule name with noweb ref. alias(<>, $.parenthesized_expression), ) #+end_src Needs to be it's own rule in order to be aliased (by tree-sitter) to a new name correctly. #+name: parenthesised_type_expr #+begin_src js :rule parenthesized_expression :arg $ :ast ParenthesizedTypeExpression seq('(', <>, ')') #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/type_expression.rs::fn parse_atom_type_expression(&mut self)][parse_atom_type_expression()]] All children here either alias calls to other literals or other AST nodes in a simple way, so they have been completely inlined. ***** TODO Path :node:cluster: :PROPERTIES: :CUSTOM_ID: h:07167116-EAE4-475B-8C87-DE9075BAF88D :END: :pgd: + [[#h:96FCF9AD-3B89-451B-B84D-90A7A625B56D][PathKind]] + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) + (opt) [[#h:D0AD78D7-3BF6-4D89-A709-C8CD28968213][Turbofish]] + (rep0 grp) =::= by [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) by (opt) [[#h:D0AD78D7-3BF6-4D89-A709-C8CD28968213][Turbofish]] :end: #+name: path #+begin_src js :rule __path :arg $ choice( 'TODO_____PATH_STUB_ALPHA', 'TODO_____PATH_STUB_BETA', ) #+end_src In all cases where Path is parsed via mentioned parsers if there are no path segments None is returned; so **Path is implied optional wherever it occurs**. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_impl(][parse_path_impl()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(crate) fn parse_path(&mut self)][parse_path()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::29][Path doc]] :lines 29-33 :src fundamental :callstack: - [-] [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::fn parse_path_impl(][parse_path_impl()]] - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::fn parse_path_kind(&mut self)][parse_path_kind()]] (is: [[#h:96FCF9AD-3B89-451B-B84D-90A7A625B56D][PathKind]]) - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::fn parse_optional_path_after_kind(][parse_optional_path_after_kind()]] :: Just a wrapper, if path has no segments and is ~PathKind::Plain~ then ~None~ is returned, else ~Some(path)~. - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::fn parse_path_after_kind(][parse_path_after_kind()]] :: Always returns a path, where the meat of the parsing is. - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::fn parse_path_generics(][parse_path_generics()]] (is: [[#h:F8EF693C-A6E2-4D57-BE08-103479D4270D][PathGenerics]]) - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::fn parse_generic_type_args(&mut self)][parse_generic_type_args()]] :end: If ~parse_path~ called allow turbofish, and allow trailing double colons are always true. *************** TODO Are PathNoTurboFish and PathTraitKind seperate Items in the parser (i.e. an Item like Path is an item)? If they are can reduce nesting complexity. *************** END ****** TODO Turbofish :node: :PROPERTIES: :CUSTOM_ID: h:D0AD78D7-3BF6-4D89-A709-C8CD28968213 :FOO: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_impl(][parse_path_impl()]] :END: :pgd: + =::= + [[#h:F8EF693C-A6E2-4D57-BE08-103479D4270D][PathGenerics]] :end: ****** SPEC PathNoTurbofish :node: :PROPERTIES: :CUSTOM_ID: h:A051D0D5-7007-4DF8-83B7-FB4EFF9C383E :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-07 Sat 16:15] :END: :pgd: + [[#h:96FCF9AD-3B89-451B-B84D-90A7A625B56D][PathKind]] + [[#h:3E35071C-E186-4562-AC61-1916E637A72E][IdentifiersInPathNoTurbofish]] :end: #+name: path_no_turbofish #+begin_src js :rule __path_no_turbofish :arg $ seq( optional(<>), <>, ) #+end_src Is implicitly optional everywhere it is used unless the callsite checks for ~None~ return. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub fn parse_path_no_turbofish(&mut self)][parse_path_no_turbofish()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_impl(][parse_path_impl()]], (see: [[#h:7BA3BCB8-65F3-4001-8B02-2F904B014F87][parse_path_after_kind]]) #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::55][PathNoTurbofish doc]] :lines 55-55 :src fundamental A /turbofish/ is syntax of the form ~::~ (i.e. generics). Function parse_path_impl called such that **trailing double colons are allowed**. *************** TODO For paths which do allow turbofish do we merge the ts rules into one thing or keep them distinct here *************** END ****** TODO IdentifiersInPathNoTurbofish :leaf: :PROPERTIES: :CUSTOM_ID: h:3E35071C-E186-4562-AC61-1916E637A72E :END: :LOGBOOK: - State "TODO" from [2025-06-07 Sat 17:00] \\ Needed because of Use quirks. :END: :pgd: + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) + (rep0 grp) =::= by [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) :end: See [[#h:ADB184CE-E43C-423B-803F-DE1679A91829][Use]] for why this node exists. We also want to collect each identifier into a nested structure so Use can be better represented in the final CST. *************** TODO This snippet useful or delete? // __identifiers_in_path_no_turbofish: ($) => prec.left(seq( // sepBy1($.identifier, '::'), // optional('::'), // )) *************** END *************** TODO Rename this heading? We're using path_kind (decomposed) since we check crate/dep/super as required but it's "depth-first" in that the deepest node has that set now instead of as some prefix. It works and it's correct but perhaps not the most obvious structure. Idk. pgd for this to be changed as well..? *************** END #+name: path_no_turbofish__nested_scopes #+begin_src js :rule __nested_scopes_in_path_no_turbofish :arg $ seq( field('scope', <>), '::', field('name', $.identifier), ) #+end_src TODO: Obviously this rule's name is wrong, but needs to be this way for now. #+name: path_no_kind_no_turbofish #+begin_src js :rule __path_no_kind_no_turbofish :arg $ seq( choice( choice($.crate, $.dep, $.super, $.identifier), // $.identifier, alias(<>, $.path), ), ) #+end_src ****** BLOCK AsTraitPath :PROPERTIES: :CUSTOM_ID: h:348C4ABC-3FC0-46EC-95A0-7F4B238BB86A :END: :LOGBOOK: - State "BLOCK" from "TODO" [2024-11-16 Sat 18:10] \\ Type and PathNoTurbofish completion. :END: + =<=. + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]]. + =as= (kw). + [[#h:A051D0D5-7007-4DF8-83B7-FB4EFF9C383E][PathNoTurbofish]]. + [[#h:3267D7A4-7AA4-49FB-91FA-A9601BC6868A][GenericTypeArgs]]. + =>=. + =::=. + ~Token::Ident~ as identifier. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_as_trait_path(&mut self)][parse_as_trait_path()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::195][AsTraitPath doc]] :lines 195-195 :src rust Not to be confused with [[#h:5B4FDF39-09B2-4C07-A2B5-3A3D6BEDD4B7][AsTraitPathType]] which wraps the return in ~AST::UnresolvedType::AsTraitPath~, this node returns a ~AST::Statement::AsTraitPath~. This is the syntax spaghetti =::Bar= stuff. Specifically the =as Trait= part which leads to the associated type. *************** TODO Add or note the docs for AsTraitPath see ast statement.rs line 394 context. *************** END ****** SPEC PathKind :leaf: :PROPERTIES: :CUSTOM_ID: h:96FCF9AD-3B89-451B-B84D-90A7A625B56D :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 16:28] - State "TOIMPL" from "TODO" [2024-11-13 Wed 18:18] :END: :pgd: - =crate= (kw) by =::= - =dep= (kw) by =::= - =super= (kw) by =::= - NIL :end: TODO: Does this mean =crate ::= is valid like =crate::= is? notice the space present TODO: Optional wrapping this or not? #+name: path_kind #+begin_src js :arg $ :ast PathKind seq( choice($.crate, $.dep, $.super), '::', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_kind(&mut self)][parse_path_kind()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::174][PathKind doc]] :lines 174-178 :src fundamental If there is no path kind, i.e. NIL, this is internally represented via ~PathKind::Plain~. ****** TOIMPL PathGenerics :node: :PROPERTIES: :CUSTOM_ID: h:F8EF693C-A6E2-4D57-BE08-103479D4270D :END: :LOGBOOK: - State "TOIMPL" from "TODO" [2024-11-13 Wed 19:28] :END: :pgd: - [[#h:3267D7A4-7AA4-49FB-91FA-A9601BC6868A][GenericTypeArgs]]<[[#h:43662F12-2EC8-47E8-B5B8-DFF8A1226EB2][OrderedTypeArg]]> :end: Checks current token is ~Token::Less~ (=<=) before continuing. Only OrderedTypeArgs are allowed, any NamedTypeArgs (aka "associated types" are errors). Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_generics(][parse_path_generics()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::157][PathGenerics doc]] :lines 157-157 :src fundamental Return ~AST::GenericTypeArg::Ordered~. *************** TODO Document in-code Noir that only OrderedGenerics are allowed. As the parsing function for PathGenerics shows, any NamedArgs will return a parser error. *************** END ****** Internals :noirc: ******* [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_after_kind(][parse_path_after_kind()]] :parse_fn: Return ~AST::Path~. Make a segments vector, check ~Token::Ident~ (i.e. check token type) and if the latter is true run the following as a loop: 1. Eat identifier (TODO: link to ~eat_ident~). 2. Parse generics ([[#h:F8EF693C-A6E2-4D57-BE08-103479D4270D][PathGenerics]]) with [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_generics(][parse_path_generics()]] **if**: - Caller allows turbofish, AND - At ~Token::DoubleColon~, AND - Next token is ~Token::Less~ (=<=). 2. Add parsed generics as ~AST::PathSegment~ to segments. 3. Back to (1) if current token (will eat) is =::= and next is ~Token::Ident~, otherwise parser error (missing identifier). Return ~AST::Path~. *************** TODO In the loop what about the allow_trailing_double_colon bit? It reads as if it allows double trailing (true) and it eats a double colon then there's an error, but wouldn't there NOT be an error? ~self.expected_identifier~ is an error no...? *************** END *************** TODO What is the error argument passed to parse_path_generics? self.parse_path_generics(ParserErrorReason::AssociatedTypesNotAllowedInPaths) Does this mean if there is an error (of any kind) then output an error of the type given as the parameter? Or does it mean if there is this error then treat it as fatal? *************** END ***** TODO Function :cluster:node: :PROPERTIES: :CUSTOM_ID: h:B3C4609F-307A-42A1-B420-DBBAB6CDE6E5 :END: :pgd: + =fn= by [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) by [[#h:BA1422E4-AB97-4099-8346-5845CA9223A1][Generics]] by [[#h:B635EAF3-0AE1-47E0-8817-7174186912D8][FunctionParameters]] + (opt grp) =->= by [[#h:17BBA21C-65D3-447D-93DE-2F13E16575C5][Visibility]] by [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] + [[#h:ED279BDF-F033-4C47-9105-6AC549CE7C31][WhereClause]] by (grp) - [[#h:B34CADE5-71B4-41F2-AB54-00483EE593A6][Block]] - =;= :end: #+name: function #+begin_src js :arg $ :ast Function seq( optional(<>), optional(<>), 'fn', field('name', $.identifier), // TODO: Generics field('parameters', <>), optional(seq('->', optional(<>), $._type)), optional(<>), choice($.block, ';'), ) #+end_src #+name: function_modifiers #+begin_src js :arg $ repeat1(choice(MODIFIERS.Unconstrained, MODIFIERS.Comptime)) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/item.rs::if self.eat_keyword(Keyword::Fn) {][parse_function() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_function(][parse_function()]] Types: [[file:noir/compiler/noirc_frontend/src/ast/function.rs::pub struct NoirFunction {][NoirFunction struct]], [[file:noir/compiler/noirc_frontend/src/ast/expression.rs::pub struct FunctionDefinition {][FunctionDefinition struct]] Mutable modifier =mut= not applicable; presence raises parse error. Function parameter =self= **not allowed** when called via ItemKind. :callstack: Check status here refers to how these functions are called at their callsite, not that they have been implemented etc (see their associated headline TODO status). - [ ] [[file:noir/compiler/noirc_frontend/src/parser/parser/traits.rs::fn parse_trait_function(&mut self)][parse_trait_function()]] - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_function(][parse_function()]] - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_function_definition(][parse_function_definition()]] - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn validate_attributes(&mut self, attributes: Vec<(Attribute, Span)>)][validate_attributes()]] :: Checks that only 1 PrimaryAttribute is set. - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_function_definition_with_optional_body(][parse_function_definition_with_optional_body()]] :: Parses identifier and onwards (generics etc) i.e. everything but attributes. - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::fn parse_generics(&mut self)][parse_generics()]] (is: [[#h:BA1422E4-AB97-4099-8346-5845CA9223A1][Generics]]) - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_function_parameters(&mut self, allow_self: bool)][parse_function_parameters()]] (is: [[#h:B635EAF3-0AE1-47E0-8817-7174186912D8][FunctionParameters]]) - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_visibility(&mut self)][parse_visibility()]] (is: [[#h:17BBA21C-65D3-447D-93DE-2F13E16575C5][Visibility]]) - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/types.rs::fn parse_type(&mut self)][parse_type()]] (is: [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]]) - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::fn parse_where_clause(&mut self)][parse_where_clause()]] (is: [[#h:ED279BDF-F033-4C47-9105-6AC549CE7C31][WhereClause]]) - [X] [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::fn parse_block(&mut self)][parse_block()]] (is: [[#h:B34CADE5-71B4-41F2-AB54-00483EE593A6][Block]]) :end: #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::34][Parser::parse_function()]] :lines 34-34 :src rust *************** TODO Look at the call to validate_attributes and document it if required Said call is within the parse_function_definition function, first expression. *************** END *************** TODO Look at type FunctionDefinitionWithOptionalBody and FunctionDefinition See the function.rs file top struct and also what parse_function_definition returns to make sure this matches what we're constructing as a function_definition in our tree-sitter grammar (e.g. whereclause, return visibility blah blah). *************** END *************** TODO Look at parse_trait_function function TraitFunction is a new type (in Noirc). *************** END ***** TODO FunctionParameters :node: :PROPERTIES: :CUSTOM_ID: h:B635EAF3-0AE1-47E0-8817-7174186912D8 :END: :pgd: + =(= + (opt) [[#h:D94BC382-4224-4FB5-8332-4C5CCF285946][FunctionParametersList]] + =)= :end: #+name: function_parameters #+begin_src js :arg $ :ast FunctionParameters seq( '(', sepBy(<>, ','), // Inlined Noirc: FunctionParametersList optional(','), ')', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_function_parameters(&mut self, allow_self: bool)][parse_function_parameters()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::134][FunctionParameters doc]] :lines 129-133 :src fundamental ****** FunctionParametersList :node: :PROPERTIES: :CUSTOM_ID: h:D94BC382-4224-4FB5-8332-4C5CCF285946 :END: :pgd: + [[#h:9554D746-C88F-4E3D-B065-B1A5C5F9B57B][FunctionParameter]] + (rep0 grp) =,= by [[#h:9554D746-C88F-4E3D-B065-B1A5C5F9B57B][FunctionParameter]] + (opt) =,= :end: **Inlined this unless something else references it in a way that requires otherwise**. Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] in parent, split by =,= until =)=, each parsed as [[#h:9554D746-C88F-4E3D-B065-B1A5C5F9B57B][FunctionParameter]]. ****** TODO FunctionParameter :node: :PROPERTIES: :CUSTOM_ID: h:9554D746-C88F-4E3D-B065-B1A5C5F9B57B :END: :pgd: + [[#h:17BBA21C-65D3-447D-93DE-2F13E16575C5][Visibility]] + [[#h:464E9BE0-4EC7-4D73-A1F2-F9C581DFD8E3][PatternOrSelf]] + =:= + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] :end: #+name: function_parameter #+begin_src js :arg $ :ast FunctionParameter seq( optional(<>), $._pattern_or_self, ':', $._type, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_function_parameter(&mut self, allow_self: bool)][parse_function_parameter()]] Types: [[file:noir/compiler/noirc_frontend/src/ast/expression.rs::pub struct Param {][Param struct]] *************** TODO Deeper parameter parsing functions Tired, perhaps parse_function_parameter, pattern_param, self_pattern_param have things of consequence in them (or not). It has ~self~ vs ~Self~ in it which is a straight identifier in the former case and a path to an identifier in the latter. *************** END ***** SPEC Visibility :node: :PROPERTIES: :CUSTOM_ID: h:17BBA21C-65D3-447D-93DE-2F13E16575C5 :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-06-10 Tue 20:09] - State "TOIMPL" from "TODO" [2025-05-15 Thu 20:36] :END: :pgd: - =pub= (kw) - =return_data= (kw) - =call_data= (kw) by =(= by [[#h:CA7F6441-C376-4A8E-9C59-B128CC1F841A][int]] (token) by =)= - NIL :end: #+name: visibility #+begin_src js :arg $ :ast Visibility choice( 'pub', 'return_data', seq('call_data(', <> ,')'), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_visibility(&mut self)][parse_visibility()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs][Visibility]] :lines 213-217 :src rust Strictly function return visibility. Default visibility is private. Within parent [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs::fn parse_function_definition_with_optional_body(][parse_function_definition_with_optional_body()]] (at: [[#h:B3C4609F-307A-42A1-B420-DBBAB6CDE6E5][Function]]) set on eventually returned ~FunctionDefinitionWithOptionalBody~: #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/function.rs][FunctionVisibility]] :lines 97-102 :src rust #+transclude: [[file:noir/compiler/noirc_frontend/src/ast/expression.rs][FunctionReturnType]] :lines 523-529 :src rust ***** TODO Expression Types: [[file:noir/compiler/noirc_frontend/src/ast/expression.rs::pub enum ExpressionKind {][ExpressionKind enum]]. ***** TODO Generics :node: :PROPERTIES: :CUSTOM_ID: h:BA1422E4-AB97-4099-8346-5845CA9223A1 :END: * (opt grp) =<= by (opt) GenericsList by =>=. **Note** Generics itself is optional, so anywhere it's referenced it implies optionality. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::pub(super) fn parse_generics(&mut self)][parse_generics()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::13][parse_generics()]] :lines 13-15 :src rust #+begin_src rust /// Generics = ( '<' GenericsList? '>' )? /// /// GenericsList = Generic ( ',' Generic )* ','? #+end_src - [X] ~Parser:parse_many()~. - nb :: splits on =,= until =>= encountered. - [ ] ~Parser:parse_generic_in_list()~. - [ ] ~Parser::parse_generic()~. ****** TODO GenericsList + Generic. + (rep0 grp) =,= by Generic. + (opt) =,=. ****** TODO Generic - VariableGeneric. - NumericGeneric. - ResolvedGeneric. [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::41][Parser::parse_generic()]]: #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs][Generic doc]] :lines 37-40 :src rust ****** TOIMPL VariableGeneric :LOGBOOK: - State "TOIMPL" from "TODO" [2024-11-03 Sun 05:25] :END: * An ~identifier~. [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::58][Parser::parse_variable_generic()]]: #+begin_src rust /// VariableGeneric = identifier #+end_src Calls ~Parser::eat_ident()~ and returns that as an ~UnresolvedGeneric::Variable~ enum. *************** TODO Appears to be some wacky macro stuff for enum ~UnresolvedGeneric~, look at way, way later. *************** END ****** TOIMPL NumericGeneric :LOGBOOK: - State "TOIMPL" from "TODO" [2024-11-03 Sun 05:26] :END: + =let=. + An ~identifier~. + =:=. + Type. [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::63][Parser::parse_numeric_generic()]]: #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs][NumericGeneric doc]] :lines 62-62 :src rust *************** TODO Parse function returns an error if missing a type after =:= (and assumes type is ~u32~) but is this error fatal? *************** END *************** TODO There's some forbidden numeric generic type logic there, look at later. *************** END ****** TODO ResolvedGeneric Foo ****** SPEC GenericTypeArgs :node:cluster: :PROPERTIES: :CUSTOM_ID: h:3267D7A4-7AA4-49FB-91FA-A9601BC6868A :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 20:16] - State "TOIMPL" from "TODO" [2024-11-13 Wed 19:10] :END: :pgd: + (grp) =<= by (opt) [[#h:8C6AF1F0-DBAC-4030-AEFC-8FBF6B069EAD][GenericTypeArgsList]] by =>= :end: TODO: If GenericTypeArgsList is referenced by anything else in addition to GenericTypeArgs, then it needs to be its own rule so we can re-use it. Here it's been inlined. #+name: generic_type_args #+begin_src js :arg $ :ast GenericTypeArgs seq( '<', sepBy(<>, ','), // Inlined Noirc: GenericTypeArgsList. optional(','), '>', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::pub(super) fn parse_generic_type_args(&mut self)][parse_generic_type_args()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::106][GenericTypeArgs doc]] :lines 106-106 :src fundamental Final part of parser loops over generics from [[#h:8C6AF1F0-DBAC-4030-AEFC-8FBF6B069EAD][GenericTypeArgsList]] checking if they are Ordered or Named and adding them to ~AST::GenericTypeArgs~ struct before that struct is returned. Return ~AST::GenericTypeArgs()~. ******* SPEC GenericTypeArgsList :node: :PROPERTIES: :CUSTOM_ID: h:8C6AF1F0-DBAC-4030-AEFC-8FBF6B069EAD :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 20:16] - State "TOIMPL" from "TODO" [2024-11-13 Wed 19:09] :END: :pgd: + [[#h:B2EDBA96-AA93-449F-A8EB-5636CCFC4F1C][GenericTypeArg]] + (rep0 grp) =,= by [[#h:B2EDBA96-AA93-449F-A8EB-5636CCFC4F1C][GenericTypeArg]] + (opt) =,= :end: Parser (sop): [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::let generics = self.parse_many(][GenericTypeArgsList parse]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::108][GenericTypeArgsList doc]] :lines 108-108 :src fundamental Split by =,= until =>=, each parsed as [[#h:B2EDBA96-AA93-449F-A8EB-5636CCFC4F1C][GenericTypeArg]]. #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::123][parse_many()]] :lines 123-127 :src rust ******* SPEC GenericTypeArg :node: :PROPERTIES: :CUSTOM_ID: h:B2EDBA96-AA93-449F-A8EB-5636CCFC4F1C :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 20:16] - State "TOIMPL" from "TODO" [2024-11-13 Wed 19:07] :END: :pgd: - [[#h:8314C368-924E-4B8B-A881-66C9F46D6833][NamedTypeArg]] - [[#h:43662F12-2EC8-47E8-B5B8-DFF8A1226EB2][OrderedTypeArg]] :end: #+name: generic_type_arg #+begin_src js :arg $ :ast GenericTypeArg choice( <>, <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::fn parse_generic_type_arg(&mut self)][parse_generic_type_arg()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::110][GenericTypeArg doc]] :lines 110-112 :src fundamental First checks if current token is ~Token::Ident~ followed by ~Token::Assign~ (===) at which point it attempts to parse NamedTypeArg. If those conditions were not true attempts to parse OrderedTypeArg. ******** SPEC NamedTypeArg :leaf: :PROPERTIES: :CUSTOM_ID: h:8314C368-924E-4B8B-A881-66C9F46D6833 :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 20:05] - State "TOIMPL" from "TODO" [2024-11-13 Wed 18:56] :END: :pgd: + ~Token::Ident~ as identifier + === + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] :end: #+name: named_type_arg #+begin_src js :arg $ :ast NamedTypeArg seq( $.identifier, '=', <>, ) #+end_src Also called "associated types". Parser: /within slice of parent/ #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::114][NamedTypeArg doc]] :lines 114-114 :src fundamental #+transclude: [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::187][GenericTypeArgs.named_args doc]] :lines 187-188 :src fundamental Return ~AST::GenericTypeArg::Named()~. Call to Type wrapped in ~parse_type_or_error~. ******** SPEC OrderedTypeArg :leaf: :PROPERTIES: :CUSTOM_ID: h:43662F12-2EC8-47E8-B5B8-DFF8A1226EB2 :END: :LOGBOOK: - State "SPEC" from "TOIMPL" [2025-05-24 Sat 20:04] \\ Effectively just an alias of TypeOrTypeExpression - State "TOIMPL" from "TODO" [2024-11-13 Wed 18:56] :END: :pgd: + [[#h:A32A351C-092B-42F1-AB03-DE49862B35D4][TypeOrTypeExpression]] :end: // _ordered_type_arg: _ => alias($.TODO_TYPE_OR_TYPE_EXPRESSION, $.ordered_type_arg) #+name: ordered_type_arg #+begin_src js :rule __ordered_type_arg :ast OrderedTypeArg 'ORDERED_TYPE_ARG___TODO' #+end_src *************** TODO How I structure TypeOrTypeExpression as a rule in the grammar directly affects this since it can simply be an alias for that *************** END Parser: /within slice of parent/ #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/generics.rs::116][OrderedTypeArg doc]] :lines 116-116 :src fundamental #+transclude: [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::184][GenericTypeArgs.ordered_args doc]] :lines 184-184 :src fundamental If attempt to parse TypeOrTypeExpression fails parser error ~ParsingRuleLabel::TypeOrTypeExpression~ is pushed. Return ~AST::GenericTypeArg::Ordered()~. *** SPEC WhereClause :node:cluster: :PROPERTIES: :CUSTOM_ID: h:ED279BDF-F033-4C47-9105-6AC549CE7C31 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-09 Mon 00:54] :END: :pgd: + =where= (kw) + (opt) [[#h:40EAA2CE-8D14-4663-B5A8-83F4D34EABF0][WhereClauseItems]] :end: #+name: where #+begin_src js :rule where_clause :arg $ seq( 'where', sepBy1(<>, ','), optional(',') ) #+end_src Is implicit optional ([[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::if !self.eat_keyword(Keyword::Where) {][src]]) everywhere it appears unless callsite checks for empty vec return. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::pub(super) fn parse_where_clause(&mut self)][parse_where_clause()]] Returns: vec of [[file:noir/compiler/noirc_frontend/src/ast/traits.rs::pub struct UnresolvedTraitConstraint {][UnresolvedTraitConstraint struct]] Note that Type returns an [[file:noir/compiler/noirc_frontend/src/ast/mod.rs::pub struct UnresolvedType {][UnresolvedType struct]], and WhereClause an UnresolvedTraitConstraint struct the latter of which has a field ~typ~ which is the former. *************** TODO parse_where_clause says constraints can be empty Does this mean =impl Bar for Foo where {}= is valid in noir then? (there's no constraint after the where, just directly to a block). *************** END **** SPEC WhereClauseItems :node: :PROPERTIES: :CUSTOM_ID: h:40EAA2CE-8D14-4663-B5A8-83F4D34EABF0 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-09 Mon 01:29] :END: :pgd: + [[#h:E13DAE9C-1546-4BEA-9F5C-F18A6497497E][WhereClauseItem]] + (rep0 grp) =,= by [[#h:E13DAE9C-1546-4BEA-9F5C-F18A6497497E][WhereClauseItem]] + (opt) =,= :end: #+name: where_clause #+begin_src js :rule where_clause_item :arg $ seq( $._type, ':', $.trait_bounds, ) #+end_src Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] in parent split on =,=. **** SPEC WhereClauseItem :node: :PROPERTIES: :CUSTOM_ID: h:E13DAE9C-1546-4BEA-9F5C-F18A6497497E :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-09 Mon 01:29] :END: :pgd: + [[#h:B3490B7C-F387-49C7-BF94-DC9CE8AC3581][Type]] + =:= + [[#h:88538872-073E-4ADA-A615-797A00E0DE5F][TraitBounds]] :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::fn parse_single_where_clause(&mut self)][parse_single_where_clause()]] If Type returns ~None~ then parser early returns ~None~ also. **** SPEC TraitBounds :node: :PROPERTIES: :CUSTOM_ID: h:88538872-073E-4ADA-A615-797A00E0DE5F :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-09 Mon 01:29] :END: :pgd: + [[#h:161848E1-1F70-4DEF-9353-4C6BAF517C82][TraitBound]] + (opt grp) =+= by [[#h:161848E1-1F70-4DEF-9353-4C6BAF517C82][TraitBound]] + (opt) =+= :end: #+name: trait_bounds #+begin_src js :arg $ :ast TraitBounds seq( sepBy1(<>, '+'), optional('+'), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::pub(super) fn parse_trait_bounds(&mut self)][parse_trait_bounds()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::51][TraitBounds doc]] :lines 50-50 :src rust Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] inline split on =+= calls [[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::fn parse_trait_bound_in_list(&mut self)][parse_trait_bound_in_list()]]. ~parse_trait_bound_in_list~ checks [[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::pub(crate) fn parse_trait_bound(&mut self)][parse_trait_bound()]] returns ~Some~ and returns that (which /is/ [[#h:161848E1-1F70-4DEF-9353-4C6BAF517C82][TraitBound]]), otherwise parse error. *************** TODO stop_if_separator_is_missing consequences Really tired right now, double check any undocumented BNF implications of this *************** END **** SPEC TraitBound :node: :PROPERTIES: :CUSTOM_ID: h:161848E1-1F70-4DEF-9353-4C6BAF517C82 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-09 Mon 01:30] :END: :pgd: + (opt) [[#h:A051D0D5-7007-4DF8-83B7-FB4EFF9C383E][PathNoTurbofish]] + [[#h:3267D7A4-7AA4-49FB-91FA-A9601BC6868A][GenericTypeArgs]] :end: #+name: trait_bound #+begin_src js :arg $ :ast TraitBound seq( optional(<>), <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/where_clause.rs::pub(crate) fn parse_trait_bound(&mut self)][parse_trait_bound()]] Returns: [[file:noir/compiler/noirc_frontend/src/ast/traits.rs::pub struct TraitBound {][TraitBound struct]] A ~TraitBound~ is a single type path used to constrain a trait. *** SPEC PatternOrSelf :node: :PROPERTIES: :CUSTOM_ID: h:464E9BE0-4EC7-4D73-A1F2-F9C581DFD8E3 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-12 Thu 17:36] :END: :pgd: + [[#h:D349E307-F033-4D2A-A729-F2EE5B483065][Pattern]] OR [[#h:5B14A337-12EC-477C-8269-961094FAB41B][SelfPattern]] :end: #+name: pattern_or_self #+begin_src js :rule _pattern_or_self :arg $ :ast PatternOrSelf choice( <>, $.self_pattern, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::pub(crate) fn parse_pattern_or_self(&mut self)][parse_pattern_or_self()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::43][PatternOrSelf doc]] :lines 43-45 :src fundamental Standard case: 1. Check next token is not =:=, then; 2. Eat current token as a ~Token::Ident~ if its literal text is =self=. PatternOrSelf forms checked in increasing complexity, absent condition checks fall through: - Standard case is checked ([[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::49][src]]). - pass :: SelfPattern =self=. - Eat =mut= (kw) and then check standard case ([[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::56][src]]). - pass :: SelfPattern =mut self=. - fail :: Pattern. - Eat =&= (token), eat =mut= (kw) and then check standard case ([[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::69][src]]). - pass :: SelfPattern =&mut self=. - fail :: Parser error ~ParserRefMutCanOnlyBeUsedWithSelf~. - All other (fall through) cases yield Pattern ([[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::88][src]]). In all cases Pattern is further parsed by call to ~parse_pattern_after_modifiers~. :verbose: 1. Construct an ~AST::UnresolvedType::Named~ with =Self= ~AST::Path~; then box the entire aforementioned into an ~AST::UnresolvedType::MutableReference~ iff given ~SelfPattern~ contained =&= (i.e. a reference). 2. Construct an ~AST::Pattern::Identifier~ with =self= ~AST::Ident~; then box the entire aforementioned into an ~AST::Pattern::Mutable~ iff given ~SelfPattern~ contained =mut= (i.e. mutable). 3. Return ~AST::Param~ which contains (1) and (2). :end: **** SPEC Pattern :node:cluster: :PROPERTIES: :CUSTOM_ID: h:D349E307-F033-4D2A-A729-F2EE5B483065 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-12 Thu 17:33] :END: :pgd: + (opt) =mut= (kw) + [[#h:4A5BB563-4244-4B1F-8084-1116B58FA40F][PatternNoMut]] :end: #+name: pattern #+begin_src js :rule _pattern :arg $ :ast Pattern seq( // alias(seq($.mut_bound, <>), $.mut_pattern), // optional($.mut_bound), <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::pub(crate) fn parse_pattern(&mut self)][parse_pattern()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::pub(crate) fn parse_pattern_after_modifiers(][parse_pattern_after_modifiers()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::fn parse_pattern_no_mut(&mut self)][parse_pattern_no_mut()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::35][Pattern doc]] :lines 35-36 :src fundamental ~parse_pattern~ optionally eats =mut= before calling ~parse_pattern_after_modifiers~ which itself optionally parses a PatternNoMut via ~parse_pattern_no_mut~ and that result is appropriately marked mutable (or not) and passed back as the return value for ~parse_pattern~. So, two levels of indirection. *************** TODO Update Noir BNF grammar comment in source code, mut is actually optional here Update Noir in-code docs, mut is actually optional. i.e. literal in-code Noir docs should say ='mut'?= and not ='mut'=. *************** END ***** SPEC PatternNoMut :node: :PROPERTIES: :CUSTOM_ID: h:4A5BB563-4244-4B1F-8084-1116B58FA40F :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-12 Thu 17:33] :END: :pgd: - [[#h:BA807BCE-99B0-4D84-BDC5-613C20F4A422][InternedPattern]] - [[#h:5EC2C25E-781B-4AA1-B01B-D37B761237F8][TuplePattern]] - [[#h:44A2D194-B244-4E4C-B53E-2FCF3F4165E2][StructPattern]] - [[#h:CE58024C-51AD-4A68-A57B-CE4E6D5C0552][IdentifierPattern]] :end: #+name: pattern_no_mut #+begin_src js :rule _pattern_no_mut :arg $ :ast PatternNoMut choice( // TODO: InternedPattern? It looks like a compiler-only internal though and not discrete syntax. <>, <>, $.identifier, // Noirc: IdentifierPattern. ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::fn parse_pattern_no_mut(&mut self)][parse_pattern_no_mut()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::112][PatternNoMut doc]] :lines 112-116 :src fundamental ***** BLOCK InternedPattern :leaf: :PROPERTIES: :CUSTOM_ID: h:BA807BCE-99B0-4D84-BDC5-613C20F4A422 :END: :LOGBOOK: - State "BLOCK" from "TODO" [2025-05-21 Wed 14:31] \\ Need to know how lexer assigns TokenKind::InternedPattern. :END: :pgd: - TERMINAL ~TokenKind::InternedPattern~. :end: Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::fn parse_interned_pattern(&mut self)][parse_interned_pattern()]] Eat token ~TokenKind::InternedPattern~ and get it's literal contents. If contents is indexable in ~noirc_arena~ (i.e. its been interned) then return ~AST::Pattern::Interned~. *************** TODO How is TokenKind::InternedPattern assigned TODO: I spent like 1 hour on the flight to BKK while really tired trying to figure out how the fuck ~TokenKind::InternedPattern~ is assigned and I see no way, maybe it's not even in-use yet or some satanic shit is involved here. Leave it for now I guess. TODO: How does the lexer assign ~TokenKind::InternedPattern~? Once that's known this node is done. TODO: TokenKind::InternedPattern (TODO) ~InteredPattern~ is a reference to an interned ~Pattern~. TODO: So a ~Spanned~ 's ~contents~ are the literal source-code buffers content for the byte region the span defines. TODO: As per the top of node_interner.rs an InternedPattern is one that is encountered specifically at comptime (i.e. in a comptime block?) TODO: PGD block for this *************** END ***** SPEC TuplePattern :node: :PROPERTIES: :CUSTOM_ID: h:5EC2C25E-781B-4AA1-B01B-D37B761237F8 :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-06-12 Thu 17:01] - State "BLOCK" from "TODO" [2024-11-13 Wed 02:23] \\ Until Path. :END: :pgd: + =(= by (opt) [[#h:3C5C665F-CB91-4C4E-9B39-ACFEE421F5DB][PatternList]] by =)= :end: #+name: tuple_pattern #+begin_src js :arg $ :ast TuplePattern seq( '(', sepBy(<>, ','), // Inlined Noirc: PatternList. optional(','), ')', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::fn parse_tuple_pattern(&mut self)][parse_tuple_pattern()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::169][TuplePattern doc]] :lines 169-169 :src fundamental ****** SPEC PatternList :node: :PROPERTIES: :CUSTOM_ID: h:3C5C665F-CB91-4C4E-9B39-ACFEE421F5DB :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-06-12 Thu 17:00] \\ Effectively an alias, could even just delete this. - State "BLOCK" from "TODO" [2024-11-13 Wed 02:22] \\ Until Path. :END: :pgd: + [[#h:D349E307-F033-4D2A-A729-F2EE5B483065][Pattern]] + (rep0 grp) =,= by [[#h:D349E307-F033-4D2A-A729-F2EE5B483065][Pattern]] + (opt) =,= :end: **Inlined this unless something else references it in a way that requires otherwise**. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::fn parse_tuple_pattern_element(&mut self)][parse_tuple_pattern_element()]] Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] in [[#h:5EC2C25E-781B-4AA1-B01B-D37B761237F8][parent]], split by =,= until =)=, each parsed as [[#h:D349E307-F033-4D2A-A729-F2EE5B483065][Pattern]]. #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::171][PatternList doc]] :lines 171-171 :src fundamental ***** SPEC StructPattern :node: :PROPERTIES: :CUSTOM_ID: h:44A2D194-B244-4E4C-B53E-2FCF3F4165E2 :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-06-12 Thu 17:26] - State "BLOCK" from "TODO" [2024-11-13 Wed 02:22] \\ Until Path is complete. :END: :pgd: + (opt) [[#h:07167116-EAE4-475B-8C87-DE9075BAF88D][Path]] + ={= + (opt) [[#h:AD401294-1A53-405B-9717-6818B89FF22E][StructPatternFields]] + =}= :end: #+name: struct_pattern #+begin_src js :arg $ :ast StructPattern seq( // TODO: Path '{', sepBy(<>, ','), // Inlined Noirc: StructPatternFields. optional(','), '}', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::fn parse_struct_pattern(&mut self, path: Path, start_span: Span)][parse_struct_pattern()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::197][StructPattern doc]] :lines 197-201 :src fundamental ****** SPEC StructPatternFields :node: :PROPERTIES: :CUSTOM_ID: h:AD401294-1A53-405B-9717-6818B89FF22E :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-06-12 Thu 17:15] - State "BLOCK" from "TODO" [2024-11-13 Wed 02:21] \\ Requires all of Pattern which itself has a dependency on Path which is TODO. :END: :pgd: + [[#h:E4A5FB8F-3DEF-4AA2-8457-E7DE648EC0F4][StructPatternField]] + (rep0 grp) =,= by [[#h:E4A5FB8F-3DEF-4AA2-8457-E7DE648EC0F4][StructPatternField]] + (opt) =,= :end: **Inlined this unless something else references it in a way that requires otherwise**. Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] in [[#h:44A2D194-B244-4E4C-B53E-2FCF3F4165E2][parent]], split by =,= until =}=, each parsed as [[#h:E4A5FB8F-3DEF-4AA2-8457-E7DE648EC0F4][StructPatternField]]. *************** TODO Update in-code Noir docs for StructPatternFields The question mark in the grouping should be an asterisk no? *************** END ****** SPEC StructPatternField :node: :PROPERTIES: :CUSTOM_ID: h:E4A5FB8F-3DEF-4AA2-8457-E7DE648EC0F4 :END: :LOGBOOK: - State "SPEC" from "BLOCK" [2025-06-12 Thu 17:15] - State "BLOCK" from "TODO" [2024-11-13 Wed 02:22] \\ Until Path is completed. :END: :pgd: + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) + (opt grp) =:= by [[#h:D349E307-F033-4D2A-A729-F2EE5B483065][Pattern]] :end: TODO: Is this similar enough to other rules we can reduce it to a single shared one? #+name: struct_pattern_field #+begin_src js :arg $ :ast StructPatternField seq( $.identifier, optional(seq(':', <>)), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::fn parse_struct_pattern_field(&mut self)][parse_struct_pattern_field()]] ***** SPEC IdentifierPattern :leaf: :PROPERTIES: :CUSTOM_ID: h:CE58024C-51AD-4A68-A57B-CE4E6D5C0552 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-12 Thu 17:31] :END: :pgd: + [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) :end: **** SPEC SelfPattern :leaf: :PROPERTIES: :CUSTOM_ID: h:5B14A337-12EC-477C-8269-961094FAB41B :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-12 Thu 16:26] :END: :pgd: + (opt) =&= by (opt) =mut= (kw) by =self= :end: #+name: self_pattern #+begin_src js :arg $ :ast SelfPattern seq( optional('&'), optional($.mut_bound), $.self, ) #+end_src Parser (sop): [[#h:464E9BE0-4EC7-4D73-A1F2-F9C581DFD8E3][PatternOrSelf]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/pattern.rs::19][SelfPattern doc]] :lines 19-19 :src rust *************** TODO Update upstream in-code docs if =&self= is in-fact valid? In-code don't suggest so but parser logic (thus far) says it is. Going to proceed as if it is for now though. *************** END :note: TODO: Claim sounds dubious based on doc comments, test if unclear when TOIMPL status. Guaranteed to NOT have a colon =:= following it. =self= is not a true keyword as it is contextual. #+transclude: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::1093][`self` is not a keyword]] :lines 1093-1094 :src rust :end: *** TODO Expression :cluster:node: :PROPERTIES: :CUSTOM_ID: h:167542CC-8CDE-49C1-9465-E5157C6A176F :END: :LOGBOOK: - State "TODO" from [2025-06-12 Thu 18:48] :END: #+name: expression #+begin_src js :rule _expression :arg $ choice( <>, <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::pub(crate) fn parse_expression_or_error(&mut self)][parse_expression_or_error()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::fn parse_expression_impl(&mut self, allow_constructors: bool)][parse_expression_impl()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/infix.rs::pub(super) fn parse_equal_or_not_equal(][parse_equal_or_not_equal()]] *************** TODO Document Literal as appropriate in org tree Currently lots of confusing names, got lexing at the top, literal at the bottom with more lexing info (which does make sense tho) and then also an AST node called Literal we should keep track of: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::/// Literal][Literal doc]] *************** END **** Infix :PROPERTIES: :CUSTOM_ID: h:ACCBF119-4052-4C4E-87CE-F7B233FF4AA1 :END: #+caption: Purely so we can insert literal =<<= and =>>= into the noweb template without it being treated as a noweb reference. #+name: ab_lit | << | >> | #+name: binary_expression #+begin_src js :arg $ :ast "EqualOrNotEqualExpression -- Entire nested hierarchy flattened and renamed." { const t = [ // Highest to lowest. [PRECEDENCE.multiplicitive, choice('*', '/', '%',)], [PRECEDENCE.additive, choice('+', '-')], [PRECEDENCE.bitshift, choice('<>', '<>')], [PRECEDENCE.comparative, choice('<', '<=', '>', '>=')], [PRECEDENCE.bitxor, '^'], [PRECEDENCE.bitand, '&'], [PRECEDENCE.or, '|'], [PRECEDENCE.equality, choice('==', '!=')], ] return choice(...t.map(([p, o]) => prec.left(p, seq( field('left', <>), field('operator', o), field('right', <>), )))) } #+end_src All the expression nodes in [[file:noir/compiler/noirc_frontend/src/parser/parser/infix.rs][infix.rs]] constitute binary expressions and their call hierarchy via [[#h:CE180901-F6F2-4A65-878D-6C9154971376][parse_infix]] denotes the reverse of their precedence. Note that the deepest call (in that file) [[file:noir/compiler/noirc_frontend/src/parser/parser/infix.rs::pub(super) fn parse_multiply_or_divide_or_modulo(][parse_multiply_or_divide_or_modulo()]] further calls [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::pub(super) fn parse_term(&mut self, allow_constructors: bool)][parse_term()]] meaning all of Term's (TODO: link) expressions are higher priority still. **** TODO QuoteExpression :node: :LOGBOOK: - State "TODO" from [2025-06-17 Tue 18:15] \\ Top-level recursion, see quote literal, deferring ultimately. :END: https://noir-lang.org/docs/noir/concepts/comptime#quasi-quote #+name: quote_expression #+begin_src js :arg $ :ast QuoteExpression alias(<>, $.quote_expression) #+end_src **** DONE ArrayExpression :node: CLOSED: [2025-06-22 Sun 02:11] :PROPERTIES: :CUSTOM_ID: h:48EA91F0-AB43-4FEF-94F1-3837C634CF38 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-22 Sun 02:11] - State "TODO" from [2025-06-21 Sat 16:38] :END: :pgd: - [[#h:D27534C1-90A8-4A0C-A601-55A631D16C89][StandardArrayLiteral]] - [[#h:437EA6B2-BF82-44D3-8B1F-ABA5BD8B3675][RepeatedArrayLiteral]] :end: #+name: array_expression #+begin_src js :arg $ :ast ArrayExpression seq( '[', choice( // Inlined Noirc: StandardArrayLiteral and ArrayElements. seq( sepBy(<>, ','), optional(','), ), // Inlined Noirc: RepeatedArrayLiteral. seq( <>, ';', field('length', <>), ), ), ']', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::if let Some(literal) = self.parse_array_literal() {][parse_array_literal() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::fn parse_array_literal(&mut self)][parse_array_literal()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::577][ArrayExpression doc]] :lines 577-577 :src fundamental #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::620][ArrayLiteral, StandardArrayLiteral, ArrayElements, RepeatedArrayLiteral docs]] :lines 620-628 :src fundamental ***** DONE StandardArrayLiteral :node: CLOSED: [2025-06-22 Sun 02:11] :PROPERTIES: :CUSTOM_ID: h:D27534C1-90A8-4A0C-A601-55A631D16C89 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-22 Sun 02:11] - State "TODO" from [2025-06-21 Sat 16:54] :END: :pgd: + =[= + (opt): + [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] + (rep0 grp) =,= by [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] + (opt) =,= + =]= :end: Here StandardArrayLiteral and ArrayElements form the same kind of "Foo" in a "FooList" parameter used elsewhere, e.g. FunctionParameter(s) and FunctionParametersList, UseTree, UseTreeList etc. Parser at parent. Returns ~ArrayLiteral::Standard~ ***** DONE RepeatedArrayLiteral :node: CLOSED: [2025-06-22 Sun 02:11] :PROPERTIES: :CUSTOM_ID: h:437EA6B2-BF82-44D3-8B1F-ABA5BD8B3675 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-22 Sun 02:11] - State "TODO" from [2025-06-21 Sat 16:54] :END: :pgd: + =[= by [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] by =;= by [[#h:0DEF3192-4840-41B3-A941-714798677092][TypeExpression]] by =]= :end: Parser at parent. Returns ~ArrayLiteral::Repeated~. **** DONE SliceExpression :node: CLOSED: [2025-06-24 Tue 16:48] :PROPERTIES: :CUSTOM_ID: h:C6FDB5DB-21D9-46A2-A4E8-DEE2AFFD1F6F :END: :LOGBOOK: - State "DONE" from [2025-06-24 Tue 16:48] :END: :pgd: + =&= by [[#h:48EA91F0-AB43-4FEF-94F1-3837C634CF38][ArrayExpression]] :end: #+name: slice_expression #+begin_src js :arg $ :ast SliceExpression seq('&', <>) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::if let Some(literal) = self.parse_slice_literal() {][parse_slice_literal() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::fn parse_slice_literal(&mut self)][parse_slice_literal()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::670][SliceExpression doc]] :lines 670-670 :src fundamental **** TODO BlockExpression :node: :PROPERTIES: :CUSTOM_ID: h:B34CADE5-71B4-41F2-AB54-00483EE593A6 :END: :LOGBOOK: - State "TODO" from [2025-05-15 Thu 20:50] :END: :pgd: + ={= by (rep0) [[#h:788ECB8C-0A48-4255-B35E-F5850902AA32][Statement]] by =}= :end: #+name: block_expression #+begin_src js :rule block :arg $ :ast BlockExpression seq( '{', repeat(<>), '}', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::if let Some(kind) = self.parse_block() {][parse_block() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::pub(super) fn parse_block(&mut self)][parse_block()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::721][Block doc]] :lines 721-721 :src fundamental Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] split by nothing until =}= each parsed as a [[#h:788ECB8C-0A48-4255-B35E-F5850902AA32][Statement]] via [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::fn parse_statement_in_block(&mut self)][parse_statement_in_block()]]. #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::727][parse_many block statements]] :lines 727-731 :src rust All statements except the last require a semicolon at the end, see [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::fn check_statements_require_semicolon(][check_statements_require_semicolon()]]. For the last statement a semicolon is optional. It is an error for non-terminal statements to NOT have a semicolon. Struct ~BlockExpression~ ([[file:noir/compiler/noirc_frontend/src/ast/expression.rs::pub struct BlockExpression {][src]]) contains vector of ~Statement~ structs, each of a ~StatementKind~ ([[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub enum StatementKind {][src]]) enum (see also: [[#h:788ECB8C-0A48-4255-B35E-F5850902AA32][Statement]]). *************** TODO Blocks without statements are not allowed in Noir But the pseudo-grammar says it is, so what is it really? *************** END **** DONE IfExpression :node: CLOSED: [2025-06-29 Sun 20:01] :PROPERTIES: :CUSTOM_ID: h:16951F18-730F-4A1F-84C9-3A1F78F67DF0 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-29 Sun 20:01] - State "TODO" from [2025-06-26 Thu 17:58] :END: :pgd: + =if= (kw) by [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] by [[#h:B34CADE5-71B4-41F2-AB54-00483EE593A6][BlockExpression]] + (opt grp) =else= (kw) by - [[#h:B34CADE5-71B4-41F2-AB54-00483EE593A6][BlockExpression]] - [[#h:16951F18-730F-4A1F-84C9-3A1F78F67DF0][IfExpression]] :end: #+name: if_expression #+begin_src js :arg $ :ast "IfStatement, IfExpression" seq( 'if', field('condition', <>), field('consequence', <>), optional(seq( 'else', field('alternative', choice(<>, <>)), )), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if let Some(kind) = self.parse_if_expr() {][parse_if_expr() parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::pub(super) fn parse_if_expr(&mut self)][parse_if_expr()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/expression.rs::442][IfExpression doc]] :lines 442-442 :src fundamental *************** TODO This has ExpressionExceptConstructor stuff similar to ForRange Refactor accordingly, if appropriate. *************** END *** TODO Statement :node: :PROPERTIES: :CUSTOM_ID: h:788ECB8C-0A48-4255-B35E-F5850902AA32 :END: :pgd: + Attributes (TODO link to) by [[#h:7BCA6405-C905-45AB-A3C7-1B7F6064454A][StatementKind]] by (opt) =;= :end: #+name: statement #+begin_src js :rule _statement_ast :arg $ :ast Statement seq( // TODO: Attributes. <>, ';', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_statement(&mut self)][parse_statement()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::25][Statement doc]] :lines 25-25 :src fundamental #+transclude: [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::36][Statement AST info]] :lines 36-38 :src fundamental Loop parsing attributes and statement kind, each statement is delimited by a semicolon. **** TODO StatementKind :node:cluster: :PROPERTIES: :CUSTOM_ID: h:7BCA6405-C905-45AB-A3C7-1B7F6064454A :END: :LOGBOOK: - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: - [[#h:71AB7D76-FD24-4B91-A187-E9968024DC0C][BreakStatement]] - [[#h:6616DE79-93E3-42AA-9633-4A29251F5BA0][ContinueStatement]] - [[#h:17935AE3-A964-49D1-927B-E67D035FD8D7][ReturnStatement]] - [[#h:FD70AFA1-5D45-434E-A9D0-7E8F6DD38058][LetStatement]] - [[#h:25216601-CAF6-4246-8289-B106F9A9166B][ConstrainStatement]] - [[#h:A0796B1B-462F-4DC6-BE7D-F1D87112AFD4][ComptimeStatement]] - [[#h:F444339D-00F1-4553-ACF6-DE0B5E2EE35E][ForStatement]] - [[#h:BB36CCC8-4FE6-4529-A365-69F0011CEC2A][IfStatement]] - [[#h:B305BFF3-A865-4292-A8E5-5448D38CE872][BlockStatement]] - [[#h:D8D0BBE8-6152-479B-8C19-282B2B56C4A9][AssignStatement]] - [[#h:12F5ADF9-E496-4D5B-896E-5CE97D44BE3D][ExpressionStatement]] :end: #+name: statement_kind #+begin_src js :rule _statement_kind :arg $ :ast StatementKind choice( <>, <>, <>, <>, <>, <>, <>, <>, <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_statement_kind(][parse_statement_kind()]] Types: [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub struct Statement {][Statement struct]] / [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub enum StatementKind {][StatementKind enum]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::59][StatementKind doc]] :lines 59-70 :src fundamental Top of parser function checks for a specially lexed ~TokenKind::InternedStatement~. If that is the next token a ~StatementKind::Interned~ of that statement is returned. Remember the return here returns at the function (i.e. ~parse_statement_kind()~ level). #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::91][TokenKind::InternedStatement check]] :lines 91-98 :src rust *************** TODO Have we done/added stuff for interned statements yet? Lex notes for interned statements also? *************** END *************** TODO See line 85:97 on ast/statement.rs Copied: // To match rust, statements always require a semicolon, even at the end of a block We'll want to take note of that for the grammar. *************** END ***** TODO Block-Ending Statements :PROPERTIES: :CUSTOM_ID: h:B617D31A-7FDE-4BB3-AB4D-5E79254585BB :END: :LOGBOOK: - State "TODO" from [2025-05-20 Tue 20:13] :END: #+begin_src js :treesit t // Statements ending in blocks, thus not requiring semicolons. _block_ending_statements: ($) => choice( $.for_statement, // $.interned_statement, // TODO: Commented temporarily. //$.block, // $.unsafe_expression, // TODO: Commented temporarily. // $.interned_expression, // TODO: Commented temporarily. // $.if_statement, // TODO: Commented temporarily. ) #+end_src Some ~StatementKind~\s do not require a terminating semicolon. Function ~StatementKind.add_semicolon~ ([[file:noir/compiler/noirc_frontend/src/ast/statement.rs::impl StatementKind { pub fn add_semicolon(][src]]) appears to handle this. ~StatementKind~ optional semicolons: - ~For~ - ~Interned~ - ~Expression~ inner ~ExpressionKind~ optional semicolons: - ~Block~ - ~Unsafe~ - ~Interned~ - ~InternedStatement~ - ~If~ :: Presence of a semicolon turns wrapping ~StatementKind~ into ~StatementKind::Semi~, otherwise (if no semicolon) it remains ~StatementKind::Expression~. ~StatementKind~ required semicolons: - ~Let~ - ~Constrain~ - ~Assign~ - ~Semi~ - ~Break~ - ~Continue~ - ~Error~ *************** TODO Comptime statement recurses a bit, do that later. See line 99 *************** END ***** DONE BreakStatement :leaf: CLOSED: [2025-06-24 Tue 17:43] :PROPERTIES: :CUSTOM_ID: h:71AB7D76-FD24-4B91-A187-E9968024DC0C :END: :LOGBOOK: - State "DONE" from "TOIMPL" [2025-06-24 Tue 17:43] - State "TOIMPL" from "TODO" [2025-05-20 Tue 19:03] - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + =break= (kw) :end: #+name: break_statement #+begin_src js :ast BreakStatement 'break' #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if self.eat_keyword(Keyword::Break) {][parse break statement parent inline]] Takes nothing (unlike say Rust where an expression can follow), only the keyword. ***** DONE ContinueStatement :leaf: CLOSED: [2025-06-24 Tue 17:47] :PROPERTIES: :CUSTOM_ID: h:6616DE79-93E3-42AA-9633-4A29251F5BA0 :END: :LOGBOOK: - State "DONE" from "TOIMPL" [2025-06-24 Tue 17:47] - State "TOIMPL" from "TODO" [2025-05-20 Tue 19:07] - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + =continue= (kw) :end: #+name: continue_statement #+begin_src js :ast ContinueStatement 'continue' #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if self.eat_keyword(Keyword::Continue) {][parse continue statement parent inline]] ***** DONE ReturnStatement :node: CLOSED: [2025-06-25 Wed 14:06] :PROPERTIES: :CUSTOM_ID: h:17935AE3-A964-49D1-927B-E67D035FD8D7 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-25 Wed 14:06] - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + =return= (kw) by (opt) [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] :end: #+name: return_statement #+begin_src js :arg $ :ast ReturnStatement seq('return', optional(<>)) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if self.eat_keyword(Keyword::Return) {][parse return statement parent inline]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::76][ReturnStatement doc]] :lines 76-76 :src fundamental *************** TODO I thought return statement is always an error in noir since no early returns *************** END *************** TODO Does the BNF doc have an error is is code missing that makes it actually optional? *************** END ***** SPEC LetStatement :node: :PROPERTIES: :CUSTOM_ID: h:FD70AFA1-5D45-434E-A9D0-7E8F6DD38058 :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-26 Thu 02:11] \\ Still needs more test coverage - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + =let= (kw) + [[#h:D349E307-F033-4D2A-A729-F2EE5B483065][Pattern]] + OptionalTypeAnnotation (TODO link) + === + [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] :end: #+name: let_statement #+begin_src js :arg $ :ast LetStatement seq( 'let', optional($.mut_bound), field('pattern', <>), // TODO: OptionalTypeAnnotation '=', field('value', <>), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if self.at_keyword(Keyword::Let) {][parse let statement parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_let_statement(&mut self, attributes: Vec<(Attribute, Span)>)][parse_let_statement()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::351][LetStatement doc]] :lines 351-351 :src fundamental *************** TODO Change rendered rule name to let_declaration? This kind of ties into using Statements as a category in the final rendered grammar.js anyway, since all _things_ are going to be at least statements anyway (or really, expressions since a statement is an expression with no return value). Semantics. *************** END *************** TODO Validate secondary attributes stuff in the let statement parse. I don't think we need to do that these attributes come from Statement, so long as we have things set correctly on Statement in terms of parsing attributes we're fine here (for the purpose of tree-sitter) I think. *************** END *************** TODO Noirc doc for pattern needs to be capital P since it's a proper AST node? *************** END ***** DONE ConstrainStatement :node: CLOSED: [2025-06-26 Thu 16:15] :PROPERTIES: :CUSTOM_ID: h:25216601-CAF6-4246-8289-B106F9A9166B :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-26 Thu 16:15] - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + =assert= (kw) OR =assert_eq= (kw) + [[#h:FB4BC935-116B-4C56-B866-3842C5CA9B5F][Arguments]] :end: #+name: constrain_statement #+begin_src js :arg $ :ast ConstrainStatement seq( // XXX: Keyword 'constrain' is deprecated, we're not going to include it at all. // 'assert' expects 1 or 2 parameters, 'assert_eq' expects 2 or 3. This is out of scope for tree-sitter grammar (at least for now), if it's a boon without huge complexity the rules can be augmented to enforce this later. choice('assert', 'assert_eq'), field('arguments', <>), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if let Some(constrain) = self.parse_constrain_statement() {][parse constrain statement parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_constrain_statement(&mut self)][parse_constrain_statement()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_constrain_kind(&mut self)][parse_constrain_kind()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::370][ConstrainStatement doc]] :lines 370-373 :src fundamental ***** DONE ComptimeStatement :node: CLOSED: [2025-06-26 Thu 17:51] :PROPERTIES: :CUSTOM_ID: h:A0796B1B-462F-4DC6-BE7D-F1D87112AFD4 :END: :LOGBOOK: - State "DONE" from "SPEC" [2025-06-26 Thu 17:51] - State "SPEC" from "TODO" [2025-06-26 Thu 16:29] \\ Doing ForStatement before tests since it depends on that - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + [[#h:5092AC79-344E-465F-973E-70DC2ECF38FB][ComptimeBlock]] OR [[#h:46D5DE09-D9EB-4227-BB31-BA77B8B0063D][ComptimeLet]] OR [[#h:B0F78DC8-11C8-4D7E-A72C-7C3498574225][ComptimeFor]] :end: #+name: comptime_statement #+begin_src js :arg $ :ast ComptimeStatement seq( 'comptime', choice( <>, // Noirc: ComptimeBlock <>, // Noirc: ComptimeLet <>, // Noirc: ComptimeFor ), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if self.at_keyword(Keyword::Comptime) {][parse comptime statement parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_comptime_statement(][parse_comptime_statement()]], [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_comptime_statement_kind(][parse_comptime_statement_kind()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::291][ComptimeStatement ComptimeBlock ComptimeLet ComptimeFor doc]] :lines 291-300 :src fundamental ****** ComptimeBlock :node: :PROPERTIES: :CUSTOM_ID: h:5092AC79-344E-465F-973E-70DC2ECF38FB :END: :pgd: + =comptime= (kw) by [[#h:B34CADE5-71B4-41F2-AB54-00483EE593A6][BlockExpression]] :end: **Inlined unless something else references it in a way that requires otherwise**. ****** ComptimeLet :node: :PROPERTIES: :CUSTOM_ID: h:46D5DE09-D9EB-4227-BB31-BA77B8B0063D :END: :pgd: + =comptime= (kw) by [[#h:FD70AFA1-5D45-434E-A9D0-7E8F6DD38058][LetStatement]] :end: **Inlined unless something else references it in a way that requires otherwise**. ****** ComptimeFor :node: :PROPERTIES: :CUSTOM_ID: h:B0F78DC8-11C8-4D7E-A72C-7C3498574225 :END: :pgd: + =comptime= (kw) by [[#h:F444339D-00F1-4553-ACF6-DE0B5E2EE35E][ForStatement]] :end: **Inlined unless something else references it in a way that requires otherwise**. ***** SPEC ForStatement :node: :PROPERTIES: :CUSTOM_ID: h:F444339D-00F1-4553-ACF6-DE0B5E2EE35E :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-06-26 Thu 17:47] \\ Still needs more test coverage - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + =for= (kw) by [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] (token) by =in= (kw) by [[#h:C859B697-6DDD-4F6D-B70B-BF0A9FBA6E80][ForRange]] by [[#h:B34CADE5-71B4-41F2-AB54-00483EE593A6][BlockExpression]] :end: #+name: for_statement #+begin_src js :arg $ :ast ForStatement seq( 'for', field('value', $.identifier), 'in', field('range', choice( <>, <>, )), field('body', <>), ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if let Some(for_loop) = self.parse_for() {][parse for parent callsite]], [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_for(&mut self)][parse_for()]] Types: [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub struct ForLoopStatement {][ForLoopStatement struct]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::227][ForStatement doc]] :lines 227-227 :src fundamental ****** DONE ForRange :node: CLOSED: [2025-06-26 Thu 17:47] :PROPERTIES: :CUSTOM_ID: h:C859B697-6DDD-4F6D-B70B-BF0A9FBA6E80 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-26 Thu 17:47] - State "TODO" from [2025-06-26 Thu 16:42] \\ ExpressionExceptConstructor look into this 'constructor' :END: :pgd: + [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] by (opt grp) - =..= by [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] - =..== by [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] :end: #+name: for_range #+begin_src js :rule range_expression :arg $ :ast ForRange seq( <>, '..', optional(token.immediate('=')), <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::fn parse_for_range(&mut self)][parse_for_range()]] Types: [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub enum ForRange {][ForRange enum]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::262][ForRange doc]] :lines 262-264 :src fundamental *************** TODO Is this the general range expression/statement for noir? Assuming it is for now and have aliased rule name as such *************** END *************** TODO ExpressionExceptConstructor Quick glance looks like no constructors means no paths or a function call, look into if we can reasonably support this in the ts grammar since it feels like it would duplicate the entire expression tree for a variant which has paths but without function/method calls. UPDATE: Constructor pattern here looks to be like =Foo {}= i.e. the braces, which I guess makes sense. So the parsing rule is expression except something that looks like it has braces and is for a Struct which would .'. be a constructor, HOWEVER block-scoped expressions should be fine, like ={1 + 1 }=, because they don't have a leading identifier =Foo { 1 + 1 }= making them NOT constructors. *************** END ***** DONE IfStatement :node: CLOSED: [2025-06-26 Thu 17:58] :PROPERTIES: :CUSTOM_ID: h:BB36CCC8-4FE6-4529-A365-69F0011CEC2A :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-26 Thu 17:58] - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + [[#h:16951F18-730F-4A1F-84C9-3A1F78F67DF0][IfExpression]] :end: **Just an alias for IfExpression**. Wraps IfExpression in ~StatementKind::Expression~. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if let Some(kind) = self.parse_if_expr() {][parse_if_expr() parent callsite]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::78][IfStatement doc]] :lines 78-78 :src fundamental ***** DONE BlockStatement :node: CLOSED: [2025-06-29 Sun 20:16] :PROPERTIES: :CUSTOM_ID: h:B305BFF3-A865-4292-A8E5-5448D38CE872 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-29 Sun 20:16] - State "TODO" from [2025-05-20 Tue 18:16] :END: :pgd: + [[#h:B34CADE5-71B4-41F2-AB54-00483EE593A6][Block]] :end: **Just an alias for Block**. Wraps Block in ~StatementKind::Expression~. Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::if let Some(block) = self.parse_block() {][parse_block() parent callsite]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/statement.rs::80][BlockStatement doc]] :lines 80-80 :src fundamental ***** TODO AssignStatement :PROPERTIES: :CUSTOM_ID: h:D8D0BBE8-6152-479B-8C19-282B2B56C4A9 :END: :LOGBOOK: - State "TODO" from [2025-05-20 Tue 18:16] :END: 2025/06/29 8:25 pm: JORDAN CONTINUE FROM HERE XXX42069XXX ***** TODO ExpressionStatement :PROPERTIES: :CUSTOM_ID: h:12F5ADF9-E496-4D5B-896E-5CE97D44BE3D :END: :LOGBOOK: - State "TODO" from [2025-05-20 Tue 18:16] :END: *** DONE Arguments :node: CLOSED: [2025-06-26 Thu 16:15] :PROPERTIES: :CUSTOM_ID: h:FB4BC935-116B-4C56-B866-3842C5CA9B5F :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-26 Thu 16:15] - State "TODO" from [2025-06-26 Thu 15:54] :END: :pgd: + =(= by (opt) [[#h:10AA2FCE-2B45-442A-8349-1161B113A661][ArgumentsList]] by =)= :end: #+name: arguments #+begin_src js :arg $ :ast Arguments seq( '(', sepBy(<>, ','), // Inlined Noirc: ArgumentsList. optional(','), ')', ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/arguments.rs::pub(crate) fn parse_arguments(&mut self)][parse_arguments()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/arguments.rs::11][Arguments ArgumentsList doc]] :lines 11-13 :src fundamental *************** TODO EBNF feels unclear here, upstream patch? assert expects 1 or 2 params, assert_eq expects 2 or 3 so how is it the case that arguments list could only ever be up to 2 elements given how ArgumentsList is specified. Have pretended it is =*= in our implementation. *************** END **** ArgumentsList :node: :PROPERTIES: :CUSTOM_ID: h:10AA2FCE-2B45-442A-8349-1161B113A661 :END: :pgd: + [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] + (rep0 grp) =,= by [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]] + (opt) =,= :end: **Inlined unless something else references it in a way that requires otherwise**. Via [[#h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA][parse_many]] in parent, split by =,= until =)=, each parsed as [[#h:167542CC-8CDE-49C1-9465-E5157C6A176F][Expression]]. *** TODO CallArguments :node: :PROPERTIES: :CUSTOM_ID: h:C98290E5-F5C5-4D52-BECC-974E4CDA07DC :END: :LOGBOOK: - State "TODO" from [2025-06-26 Thu 15:54] :END: :pgd: + =!= by [[#h:FB4BC935-116B-4C56-B866-3842C5CA9B5F][Arguments]] :end: #+name: call_arguments #+begin_src js :arg $ :ast CallArguments seq( optional('!'), <>, ) #+end_src Parser: [[file:noir/compiler/noirc_frontend/src/parser/parser/arguments.rs::pub(super) fn parse_call_arguments(&mut self)][parse_call_arguments()]] #+transclude: [[file:noir/compiler/noirc_frontend/src/parser/parser/arguments.rs::28][CallArguments doc]] :lines 28-28 :src fundamental Looks to specify a macro call if =!(= is the sequence of initial tokens. *** TODO Literals :PROPERTIES: :CUSTOM_ID: h:3446FD3E-4526-4DBF-80AB-64BD9078C4F5 :END: Constructed by the lexer. *************** TODO Make this tree (from Literals) about literals and tokens, generally things the lexer sets TokenKind on *************** END **** Token Lexing Function [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn next_token(&mut self)][Lexer.next_token()]] is the major fork where ~Token~\s are created. After a series of whitespace is ignored attempts to lex [[#h:47747650-8CB5-48B1-A085-AFAF3C469B8B][Glue]], and single-character tokens are made followed by more complex tokens. (TODO all these properly as appropriate): eat_string_literal for string literal escape sequences (e.g. \n). eat_format_string_or_alpha_numeric format strings or..? eat_raw_string_or_alpha_numeric raw strings 'r' and '#' or something..? eat_quote_or_alpha_numeric quote strings starting with 'q' eat_attribute - =#= for [[#h:22DB13A6-D91E-4240-8711-10ED6DEE1C6E][attribute]] ([[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_attribute(&mut self)][eat_attribute()]]) eat_alpha_numeric called when character is_ascii_alphanumeric (rust stdlib function) or '_' then ~A-Za-z_~ calls to eat_word eat_word calls lex_word and then lookup_word_token lex_word keeps eating while rust stdlib is_ascii_alphabetic (A-Za-z) or rust is_numeric (0-9) or _ Lookup word token checks if the word is a keyword via ~Keyword::lookup_keyword~ (TODO: Nice list of keywords). Checks if its an int type ~IntType::lookup_int_type~. If both of those fail its then an identifier ~Token::Ident~. We don't need to give tree-sitter a literal list of keywords since we can rely on automatic keyword extraction (TODO: doc this here, the ts keyword extraction and how we set it in the grammar file). So, our identifier syntax is ~A-Za-z0-9_~ of which those not consumed as ~Token::IntType~. Looking at lookup_int_type this means literal numbers prefixed with u or i will be int types. We probably don't need a regex to exclude these since we can rely on precedence. Via the lexer logic we clearly see that attempting to parse an int type is higher precedence (checks and returns first, last resort is identifier). *************** TODO How is that for result in lexer thing even populated (top of Lexer struct) Once that's clear all the call syntax etc up to Lexer.next_token can be documented. Perhaps just ask in Rust IRC. *************** END **** Glue :PROPERTIES: :CUSTOM_ID: h:47747650-8CB5-48B1-A085-AFAF3C469B8B :END: Lexer (master): [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn glue(&mut self, prev_token: Token)][glue()]] Some tokens can contain more than 1 character, e.g. =!== (not equal-to), such tokens are formed by being 'glued' together during lexing. Starting glue tokens are: =<=, =>=, ===, =/=, =.=, =:=, =!=, =-=. #+transclude: [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::115][Lexer.next_token() glue matches]] :lines 115-122 :src fundamental Since ~next_token~ (TODO: link) has already incremented the cursor when the [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn glue(&mut self, prev_token: Token)][glue()]] lexer is called (in next_tokens pattern match arm), glue is given the prior (initial) ~Token~ it is to start attempting to match from and begins subsequently. *************** TODO Copy prior notes on glue from elsewhere to here I've taken notes on this before, put glue stuff here as appropriate. *************** END **** DONE comment :lex: CLOSED: [2025-06-05 Thu 16:39] :PROPERTIES: :CUSTOM_ID: h:E866B8E1-2736-4A62-938B-0FD416C4A088 :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-05 Thu 16:39] - State "TODO" from [2025-05-25 Sun 18:05] \\ Need to document how this is lexed since parser grabs it by Token type only. :END: #+name: comment #+begin_src js :arg $ choice( <>, <>, ) #+end_src Lexer (sop): [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::222][glue() Token::Slash match arm]] Look at next char, if: - =/= switch to [[#h:155F78B1-495B-4F49-BFED-82369979A23E][line_comment]], else if - =*= switch to [[#h:5B512D17-7FD5-4FD5-B4AA-C1B27A9E6FCA][block_comment]] Both lexers for line/block comment are called with the start position on the character /after/ their (now completed) glue-token. Comments can optionally have a [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::pub enum DocStyle {][DocStyle enum]], ~Outer~ or ~Inner~. ~Outer~ means the comment applies to the subsequent AST node after it (i.e. immediately preceding the element it is outside of). ~Inner~ means the comment applies to its parent AST node (i.e. the element it is within). **Implemented in =extras= as comments can appear anywhere in code.** *************** TODO Outer comments are... and Inner comments are...? *************** END ***** DONE line_comment CLOSED: [2025-05-27 Tue 19:08] :PROPERTIES: :CUSTOM_ID: h:155F78B1-495B-4F49-BFED-82369979A23E :END: :LOGBOOK: - State "DONE" from "TODO" [2025-05-27 Tue 19:08] - State "TODO" from [2025-05-25 Sun 19:00] :END: :pgd: + =//= + (opt grp): - =!= (marks ~DocStyle::Inner~) - =/= (marks ~DocStyle::Outer~) + ASCII until newline :end: #+name: line_comment__doc_style__inner #+begin_src js :rule __inner_line_comment_doc_style token.immediate(prec(2, '!')) #+end_src #+name: line_comment__doc_style__outer #+begin_src js :rule __outer_line_comment_doc_style token.immediate(prec(2, '/')) #+end_src #+name: line_comment__doc_style #+begin_src js :rule __line_comment_doc_style :arg $ choice( alias(<>, $.inner_doc_style), alias(<>, $.outer_doc_style), ) #+end_src #+name: line_comment #+begin_src js :arg $ seq( '//', choice( // Four forward-slashes is still a normal line comment, not an outer-style. seq(token.immediate(prec(2, '//')), /.*/), seq( field('style', <>), field('content', alias(/.*/, $.doc_comment)), ), /.*/, ), ) #+end_src Lexer: [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn parse_comment(&mut self, start: u32)][parse_comment()]] Continue eating asii characters (via Rust stdlib ~is_ascii~) until newline is encountered. It looks like quadruple forward-slash =////= will disqualify a comment from having ~DocStyle::Outer~, it must be exactly triple. [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::Some('/') if self.peek2_char() != '/'.into() => {][See this line of the lexer]]. So =////= has no DocStyle. *************** TODO It looks like comments are lexed (and then parsed) WITHOUT the comment prefixes So a comment like // Foo is parsed as "Foo" (but we know its a comment), the leading characters are dropped. Shall we do this too for ts? Likely we have a comment node and then a "value" or "contents" field on it which is everything after the leading token type *************** END ***** DONE block_comment CLOSED: [2025-06-04 Wed 19:29] :PROPERTIES: :CUSTOM_ID: h:5B512D17-7FD5-4FD5-B4AA-C1B27A9E6FCA :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-04 Wed 19:29] - State "TODO" from [2025-05-25 Sun 19:03] :END: :pgd: + =/*= + (opt grp): - =!= (marks ~DocStyle::Inner~) - =*= (marks ~DocStyle::Outer~) + ASCII until balanced =*/= :end: #+name: block_comment__doc_style #+begin_src js :rule __block_comment_doc_style :arg $ choice( alias($.__inner_block_comment_doc_style, $.inner_doc_style), alias($.__outer_block_comment_doc_style, $.outer_doc_style), ) #+end_src #+name: block_comment #+begin_src js :arg $ seq( '/*', optional( choice( // Block comments with doc style (see external parser). seq( field('style', <>), optional(field('content', alias($._block_comment_content, $.doc_comment))), ), // Normal block comments (see external parser). $._block_comment_content, ), ), '*/', ) #+end_src Lexer: [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn parse_block_comment(&mut self, start: u32)][parse_block_comment()]] Continue eating ascii characters (via Rust stdlib ~is_ascii~) until =*/= encountered such that all opening =/*= and closing =*/= pairs are balanced. It looks like an empty ~DocStyle::Outer~ block comment is not possible, so =/***/=. It must at least have some content =/** foo */=. [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::Some('*') if !matches!(self.peek2_char(), Some('*' | '/')) => {][See this line of the lexer]]. So =/***/= has no DocStyle. *************** TODO It looks like outer doc comments must be exactly two asterisks like how outer line comments but be exactly three slashes, right? Confirm this later, also feeling a bit tired right now so could just be missing due to fatigue. For now will assume this is true. *************** END *************** TODO Nested block comments are not multiple AST nodes but a single block comment then? Looks like it but confirm later on *************** END **** ident :lex: :PROPERTIES: :CUSTOM_ID: h:65B0176B-B46F-4679-8535-C504870AC048 :END: Appears only place ~Token::Ident~ is created is within [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn lookup_word_token(][lookup_word_token()]]. ~Token::Ident~. Noir does not support Unicode Identifiers (UAX#31) so XID_Start/XID_Continue. Only ASCII. #+name: identifier #+begin_src js :ast identifier /[a-zA-Z_][a-zA-Z0-9_]*/ #+end_src **** AttributeKeywords :PROPERTIES: :CUSTOM_ID: h:0845FBC0-576F-4E44-B2A8-B7D7B17FBC84 :END: Also called FunctionAttribute (predefined): #+name: primary_attributes #+begin_src js // Functions can only have one primary attribute. const PRIMARY_ATTRIBUTES = [ 'foreign', 'builtin', 'oracle', 'test', 'recursive', 'fold', 'no_predicates', 'inline_always', 'test', 'field', ] #+end_src Include CustomAttribute and InnerAttribute (predefined): #+name: secondary_attributes #+begin_src js // Functions can have any number of secondary attributes. const SECONDARY_ATTRIBUTES = [ 'deprecated', 'contract_library_method', 'abi', 'export', 'varargs', 'use_callers_scope', 'allow', ] #+end_src **** DONE attribute :lex: CLOSED: [2025-06-05 Thu 20:02] :PROPERTIES: :CUSTOM_ID: h:22DB13A6-D91E-4240-8711-10ED6DEE1C6E :END: :LOGBOOK: - State "DONE" from "TODO" [2025-06-05 Thu 20:02] - State "TODO" from [2025-06-05 Thu 17:37] \\ Reviewing attribute lexing :END: :pgd: + =#= + (opt) =!= (marks ~Token::InnerAttribute~) + =[= + (opt) ='= (marks as a tag) + CHARS until first =]= :end: #+name: attribute #+begin_src js :rule attribute_item :arg $ :ast Attributes, InnerAttribute seq( '#', optional('!'), // Marks InnerAttribute. '[', optional("'"), // Marks attribute as having a tag alias(<>, $.content), ']', ) #+end_src #+name: attribute_content #+begin_src js seq(repeat1(choice(' ', REG_ALPHABETIC, REG_NUMERIC, REG_ASCII_PUNCTUATION))) #+end_src Lexer: [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_attribute(&mut self)][eat_attribute()]], [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::pub(crate) fn lookup_attribute(][Attribute::lookup_attribute()]] Types: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::pub enum Attribute {][Attribute enum]] / [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::pub enum FunctionAttribute {][FunctionAttribute enum]] / [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::pub enum SecondaryAttribute {][SecondaryAttribute enum]] / [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::pub struct CustomAttribute {][CustomAttribute struct]] If next immediate char is =!= it's a ~Token::InnerAttribute~, else a (normal) ~Token::Attribute~. The contents of the attribute are everything within the starting/ending =[= and =]=, excluding the tag mark (if present). In source this is ~word~, and ~span~ is everything from the starting =#= to closing =]= inclusive. After an all the characters of an attribute have been eaten (but before a concrete ~Token~ is created) [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::pub(crate) fn lookup_attribute(][Attribute::lookup_attribute()]] is called to check if this is a predefined attribute, or a custom one. Parameter ~word~ split into segments at =(= and =)=, all empty segments dropped: #+transclude: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::754][lookup_attribute() word split]] :lines 754-757 :src rust Parameter ~span~ has all characters checked such that they are any of: - ASCII alphabetic. - Numeric :: *NOTE* that this does NOT mean ASCII numeric, see [[https://doc.rust-lang.org/std/primitive.char.html#method.is_numeric][rust docs]]. - ASCII punctuation; any of (see [[https://doc.rust-lang.org/std/primitive.char.html#method.is_ascii_punctuation][rust docs]]): ~! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~~. - ASCII whitespace. #+transclude: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::759][lookup_attribute() slice check]] :lines 759-771 :src rust If attribute is a tag (via parameter ~is_tag~) an ~Attribute::Secondary~ (enumerant) is returned, itself containing a ~SecondaryAttribute~ (enum) with it's ~Tag~ set to a ~CustomAttribute~. #+transclude: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::773][lookup_attribute() tag return]] :lines 773-779 :src rust Otherwise each of the processed ~word~\s is checked to see if it's a predefined attribute and either a ~FunctionAttribute~ (enum) or ~SecondaryAttribute~ (enum) is returned as appropriate ([[file:noir/compiler/noirc_frontend/src/lexer/token.rs::781][lookup_attribute() top of predefined pattern-match]]). Penultimately if =!= was present (marking a ~Token::InnerAttribute~) and the return from lookup_attribute was a ~FunctionAttribute~ a lex error is thrown. If it was a ~SecondaryAttribute~ that return value is wrapped into a ~Token::InnerAttribute~ and returned. Otherwise a ~Token::Attribute~ wrapping the return value is returned. So ~Token::InnerAttribute~ cannot have the name of any ~FunctionAttribute~ (aka. Primary) attribute. ~Token::Attribute~ /can/ have the name of any ~FunctionAttribute~ (aka. Primary) or ~SecondaryAttribute~. *************** TODO // TODO: Differentiate between inner/non-inner in grammar, for now not doing so in order to focus on completing grammar entirely (broadly). *************** END *************** TODO Is the field attribute primary or secondary? In source its listed as Attribute::Secondary but the comment for secondary attributes begins after it. For now I've taken it as the comment describes (thus considering it a primary attribute). *************** END **** SPEC bool :lex: :PROPERTIES: :CUSTOM_ID: h:54ACA91F-70A8-4121-A799-F872DDCAF240 :END: :LOGBOOK: - State "SPEC" from [2025-06-15 Sun 17:15] :END: :pgd: - =true= (kw) - =false= (kw) :end: Via [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::fn eat_bool(&mut self)][eat_bool]] eats a token which was lexed as ~Token::Bool~ which are created from either =true= or =false= (see: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::pub(crate) fn lookup_keyword(word: &str)][lookup_keyword()]]). #+name: bool #+begin_src js :rule bool_literal :ast bool choice('true', 'false') #+end_src *************** TODO Put lookup_keyword into Internal alongside parse_many and friends if it keeps being called in other places we document (like it is here). *************** END **** SPEC int :lex: :PROPERTIES: :CUSTOM_ID: h:CA7F6441-C376-4A8E-9C59-B128CC1F841A :END: :LOGBOOK: - State "SPEC" from "TODO" [2025-05-26 Mon 12:30] - State "TODO" from [2025-05-26 Mon 12:30] :END: :pgd: - TODO :end: #+name: int #+begin_src js :rule int_literal :ast int token(seq( choice( /[0-9][0-9_]*/, /0x[0-9a-fA-F_]+/, ) )) #+end_src Lexer: ~Token::Int~ *************** TODO Put this TODO elsewhere, this describes a Token::Int Note a Token::Int is not the same as a Token::IntType. Token::Int is a literal integer e.g. 42 whereas Token::IntType is say u32 or i8 etc. eat_alpha_numeric -> eat_digit eat_alpha_numeric if the initial char is 0..9 call eat_digit eat_digit continues to eat while each character is ascii digit, or ascii hexdigit, or 'x' or '_'. Underscore cannot appear at the end (invalid). Underscore at start is an identifier not a Token::Int. Cannot have multiple consecutive underscores. Appears to be an error for: let integer_str = self.eat_while(Some(initial_char), |ch| { ch.is_ascii_digit() | ch.is_ascii_hexdigit() | (ch == 'x') | (ch == '_') }); This means we can have integers like 0xxxx123 be valid? Or 0x123x123x123 (note the multiple x). Should also link stuff as appropriate. This is lexing documentation. *************** END *************** TODO Update reg for error on multiple consecutive _ and if a trailing _ As it is is fine for now however. *************** END **** DONE str :lex: CLOSED: [2025-06-15 Sun 19:09] :PROPERTIES: :CUSTOM_ID: h:6135F05F-8F23-4B77-98E2-CF6D639F4D7A :END: :LOGBOOK: - State "DONE" from [2025-06-15 Sun 19:09] :END: #+name: str #+begin_src js :rule str_literal :arg $ :ast str seq( '"', repeat(choice( <>, <>, // $.str_content, // $.escape_sequence, )), token.immediate('"'), ) #+end_src #+name: escape_sequence #+begin_src js :arg $ seq( '\\', // TODO: Do we want to be strict on valid escape sequences (r, n, t etc) or accept any ASCII. Problem is error recovery in tree-sitter and how that affects highlighting. token.immediate(choice( 'r', 'n', 't', '0', '"', '\\', )), ) #+end_src Whitespace characters, and printable ASCII except " (x22) and \ (x5C). #+name: str_content #+begin_src js /[\x20-\x21\x23-\x5B\x5D-\x7E\s]+/ #+end_src Lexed via [[#h:2AE110C8-8650-4F3B-AAA0-FFE5E79C8C81][next_token]] pattern ="= calls [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_string_literal(&mut self)][eat_string_literal()]] which lexes and returns a ~Token::Str~. Parsed via [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::fn eat_str(&mut self)][eat_str]] which eats a token which was lexed as ~Token::Str~. Lexer eats until ending ="= allowing the following escape characters within the string: =\r=, =\n=, =\t=, =\0=, =\"=, =\\=. *************** TODO Keep as-is or use an external scanner? Seems to be fine for now. *************** END **** DONE raw_str :lex: CLOSED: [2025-06-17 Tue 16:40] :PROPERTIES: :CUSTOM_ID: h:0573AE7E-F170-4CC3-BE8C-852249558A04 :END: :LOGBOOK: - State "DONE" from [2025-06-17 Tue 16:40] :END: #+name: raw_str #+begin_src js :rule raw_str_literal :arg $ :ast rawstr seq( $._raw_str_literal_start, alias($._raw_str_literal_content, $.str_content), $._raw_str_literal_end, ) #+end_src Lexed via [[#h:2AE110C8-8650-4F3B-AAA0-FFE5E79C8C81][next_token]] pattern =r= calls [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_raw_string_or_alpha_numeric(&mut self)][eat_raw_string_or_alpha_numeric()]] which peeks at the following 2 characters. If they are (order matters) of the following patterns: (=#=, =#=) OR (=#=, ="=) OR (="=, ANYTHING) then [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_raw_string(&mut self)][eat_raw_string()]] is called which finalises the lexing and returns a ~Token::RawStr~. If none of those patterns are matched then [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_alpha_numeric(&mut self, initial_char: char)][eat_alpha_numeric()]] is called which will attempt to eat the span via ~eat_word~, which see. ~eat_raw_string~ eats all leading =#= and records the count. Technically over 255 leading =#= is an error. A ="= must immediately follow after the pounds. Loop doing: eat characters until ="= at which point all the =#= after the ="= are eaten and counted. If the count of these pounds equals the leading ones that terminates the raw string literal, otherwise the ="= and eaten =#= are also string contents and the loop continues. Essentially this all means that a raw string literal is started via =r"= or =r#"= where there can be 0 to 255 =#=\s. The amount of =#=\s form the delimiter. The raw string literal only ends when an ="= or ="#= is encountered, in the latters case where the amount of =#=\s matches the starting delimiter exactly. No escape sequences are handled at all. Parsed via [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::fn eat_raw_str(&mut self)][eat_raw_str]] which eats a token which was lexed as ~Token::RawStr~. **** DONE fmt_str :lex: CLOSED: [2025-06-17 Tue 17:43] :PROPERTIES: :CUSTOM_ID: h:196B9814-8776-49F7-B43A-6084A8A3256A :END: :LOGBOOK: - State "DONE" from [2025-06-17 Tue 17:43] :END: #+name: fmt_str #+begin_src js :rule fmt_str_literal :arg $ :ast fmtstr seq( 'f"', repeat(alias(<>, $.str_content)), token.immediate('"'), ) #+end_src Whitespace characters, and printable ASCII except " (x22). #+name: fmt_str_content #+begin_src js /[\x20-\x21\x23-\x7E\s]+/ #+end_src Lexed via [[#h:2AE110C8-8650-4F3B-AAA0-FFE5E79C8C81][next_token]] pattern =f= calls [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_format_string_or_alpha_numeric(&mut self)][eat_format_string_or_alpha_numeric()]] which peeks at the next character, only if it is ="= is [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_fmt_string(&mut self)][eat_fmt_string()]] called which simply eats all characters until first ="= encountered. No escape sequences appear to be interpreted, so =\"= within the string contents will result in an error. Parsed via [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::fn eat_fmt_str(&mut self)][eat_fmt_str]] which eats a token which was lexed as ~Token::FmtStr~. **** TODO quote :lex: :PROPERTIES: :CUSTOM_ID: h:3F001F84-17D8-4B0D-912F-67AF98F72CD4 :END: :LOGBOOK: - State "TODO" from [2025-06-17 Tue 18:10] \\ For now will eat arbitrary characters, without nesting, until rest of grammar is completed. :END: :pgd: + =quote= + ={= OR =[= OR =(= + ANYTHING next_token CAN VALIDLY LEX (TODO: Expand and represent this) + =}= OR =]= OR =)= (matching the opening) :end: // TODO: This isn't /really/ a literal (in terms of where we've placed it here) as quote allows all things next_token can lex and so we'll want to parse strings, variables etc all over again within the quote area. For now we'll make it a very dumb stubbed rule since this really depends on everything else and we'll want everything else working so we can develop and evaluate proper coverage of this meta-programming/macro rule. // TODO: https://noir-lang.org/docs/noir/concepts/comptime#quasi-quote // TODO: Will likely require an external scanner since we need to match the opening delimiter which can be 1 of 3 options, and there is nesting. For now: only braces. #+name: quote #+begin_src js :rule quote_literal :ast quote seq( // TODO: Stubbed for now, see org doc. 'quote', '{', /.*/, '}', ) #+end_src Lexed via [[#h:2AE110C8-8650-4F3B-AAA0-FFE5E79C8C81][next_token]] pattern =q= calls [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn eat_quote_or_alpha_numeric(&mut self)][eat_quote_or_alpha_numeric()]] which eats =quote= (via [[#h:E76126FE-1E7A-40E9-A0C2-14F3FD3DB7E5][lex_word]]). Get the delimiter type via ~next_token~, where braces (={}=), brackets (=[]=), and parentheses (=()=) are valid delimiters. Then, while keeping track of potential delimiter nesting, consume tokens via ~next_token~. Parsed via [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::fn eat_quote(&mut self)][eat_quote]] which eats a token which was lexed as ~Token::Quote~. ** Internal *** [[file:noir/compiler/noirc_frontend/src/parser/parser/parse_many.rs::9][Parser::parse_many(items, sep)]] :PROPERTIES: :CUSTOM_ID: h:8E0DC05B-1ED6-47BE-9589-64DC06FAECCA :END: Parses list of items separated by sep, can optionally end when another (different configurable) separator is found. - [ ] ~Parser::parse_many_return_trailing_separator_if_any()~. TODO: Don't think I need to document parse_many_return blah blah, that's a compiler implementation detail (unless in future we have problems relating to it). Accepts a single trailing separator (up to callsite if it allows trailing or not). *** [[file:noir/compiler/noirc_frontend/src/parser/parser.rs::fn eat_kind(&mut self, kind: TokenKind)][Parser.eat_kind(kind)]] :PROPERTIES: :CUSTOM_ID: h:AC5A6E2C-59EC-44C6-9D1E-52553E56C3F9 :END: Parameter =kind= (~TokenKind~) is compared to the parsers current token kind, and if they match ~Some(current_token)~ is returned and the parser is incremented one token forward (via bump). Checking token kind goes down to [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::355][SpannedToken.kind()]] to [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::497][Token.kind()]] which is the following pattern match: #+transclude: [[file:noir/compiler/noirc_frontend/src/lexer/token.rs::498][Token.kind() pattern match]] :lines 498-522 :src rust *** [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_after_kind(][Parser::parse_path_after_kind(kind, allow_turbofish, allow_trailing_double_colon)]] :PROPERTIES: :CUSTOM_ID: h:7BA3BCB8-65F3-4001-8B02-2F904B014F87 :END: Can be reached via [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_path_impl(][Parser::parse_path_impl()]] and [[file:noir/compiler/noirc_frontend/src/parser/parser/path.rs::pub(super) fn parse_optional_path_after_kind(][Parser::parse_optional_path_after_kind()]] which are simple wrappers. Parses the rest of a path after the [[#h:96FCF9AD-3B89-451B-B84D-90A7A625B56D][PathKind]] and always returns a path even if it's still just the PathKind and nothing else. Loops [[#h:65B0176B-B46F-4679-8535-C504870AC048][ident]] tokens after the PathKind, if parameter ~allow_turbofish~ is true =::= and =<= are eaten and [[#h:F8EF693C-A6E2-4D57-BE08-103479D4270D][PathGenerics]] are parsed. Each ident forms a [[file:noir/compiler/noirc_frontend/src/ast/statement.rs::pub struct PathSegment {][PathSegment struct]]. It is possible to eat no idents, returning an empty path. *** [[file:noir/compiler/noirc_frontend/src/parser/parser/infix.rs::fn parse_infix(][Parser.parse_infix(allow_constructors, next, op)]] :PROPERTIES: :CUSTOM_ID: h:CE180901-F6F2-4A65-878D-6C9154971376 :END: Param ~next~ is a function name to be called, and param ~op~ is a lambda to be executed **after** ~next~ is executed, possibly multiple times. Each time ~parse_infix~ is called it updates Parser's ~start_span~ to ~current_token_span~. Then ~next~ is executed, and a loop over ~op~ is created. For all the expression AST nodes in the selfsame file ~parse_infix~ is in they pass a ~next~ which results in calling ~parse_infix~ again. This means for those AST nodes the operator precedence (for the language grammar) is the inverse of their call hierarchy. So ==== and =!== have the least precedence, and =*=, =/=, =%= the most. *************** TODO My understanding of the precedence here is correct yeah? *************** END *** [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn next_token(&mut self)][Lexer.next_token()]] :PROPERTIES: :CUSTOM_ID: h:2AE110C8-8650-4F3B-AAA0-FFE5E79C8C81 :END: Major fork where ~Token~\s are created. After a series of whitespace is ignored: attempts to lex [[#h:47747650-8CB5-48B1-A085-AFAF3C469B8B][Glue]], and single-character tokens are made followed by more complex tokens. *** [[file:noir/compiler/noirc_frontend/src/lexer/lexer.rs::fn lex_word(&mut self, initial_char: char)][Lexer.lex_word(initial_char)]] :PROPERTIES: :CUSTOM_ID: h:E76126FE-1E7A-40E9-A0C2-14F3FD3DB7E5 :END: From (and including) parameter =initial_char= eat characters which are ascii alphabetic (Rust stdlib), or numeric (Rust stdlib) or =_= and return the start/end positions of the eaten characters and said characters. * Template Constructed grammar from investigating Noir compiler frontend. #+begin_src js :tangle grammar.js :noweb yes const REG_ALPHABETIC = /[a-zA-Z]/ // TODO: Using this where Noir parser frontend checks for numeric which actually includes unicode stuff also, but the rest of Noir only allows ASCII so.. this is probably fine. const REG_NUMERIC = /[0-9]/ const REG_ASCII_PUNCTUATION = /[!"#$%&'()*+,\-./:;<=>?@\[\\\]^_`\{|\}~]/ // TODO: Put this in appropriate tangle location instead of literally part of the tangle template maybe? See PrimitiveType > IntegerType. const INTEGER_TYPES = [ 'u1', 'u8', 'u16', 'u32', 'u64', // 'i1', 'i8', 'i16', 'i32', 'i64', ] const PRECEDENCE = { // TODO: Term's even-higher precedence items. unary: 10, multiplicitive: 9, additive: 8, bitshift: 7, comparative: 6, bitxor: 5, bitand: 4, or: 3, equality: 2, } <> <> <> module.exports = grammar({ name: 'noir', externals: ($) => [ $._raw_str_literal_start, $._raw_str_literal_content, $._raw_str_literal_end, // Block comments are `extras` and can occur anywhere, so must be last in this list otherwise they will clobber other tokens we use an external scanner for. $._block_comment_content, $.__inner_block_comment_doc_style, $.__outer_block_comment_doc_style, ], extras: ($) => [ /\s/, $.line_comment, $.block_comment, ], // TODO: Need to document (for myself) keyword extraction to check we're doing it properly. word: ($) => $.identifier, rules: { // Noirc: Module -- top-level AST node is really Program but it immediately wraps Module. source_file: ($) => repeat($._statement), // Can statement-ise anything so we'll use this as top-level. // _statement: ($) => choice($._expression_statement, $._declaration_statement), // _expression_statement: ($) => seq($._expression, ';'), _statement: ($) => choice($.__item), // Noirc: Module -- Since doc comments can appear anywhere, Module is Item which is ItemKind. __item: ($) => choice( <>, <>, <>, <>, <>, <>, ), item_list: ($) => seq( '{', repeat($.__item), '}', ), // * * * * * * * * * * * * * * * * * * * * * * * * * DECLARATIONS / ITEMS <> // * * * * * * * * * * * * * * * * * * * * * * * * * STATEMENT KINDS (AST) // TODO: Consider all Noirc 'statements' except we enforce trailing semicolon where required? Or just have a statements section idk yet. <> // * * * * * * * * * * * * * * * * * * * * * * * * * EXPRESSIONS <> // * * * * * * * * * * * * * * * * * * * * * * * * * TYPES <> // * * * * * * * * * * * * * * * * * * * * * * * * * PATTERNS <> // * * * * * * * * * * * * * * * * * * * * * * * * * LITERALS <> mut_bound: _ => 'mut', self: _ => 'self', crate: _ => 'crate', dep: _ => 'dep', super: _ => 'super', }, }) // Match one or more occurrences of rule separated by sep. function sepBy1(rule, sep) { return seq(rule, repeat(seq(sep, rule))) } // Match zero or more occurrences of rule separated by sep. function sepBy(rule, sep) { return optional(sepBy1(rule, sep)) } #+end_src ** Declarations #+name: grp__declarations #+begin_src js :noweb yes <> <> <> <> <> <> <> // TODO: Trait <> // TODO: TypeAlias <> <> #+end_src ** Statements #+name: grp__statements #+begin_src js :noweb yes <> <> #+end_src ** Expressions #+name: grp__expressions #+begin_src js :noweb yes <> <> <> <> <> <> <> <> // TODO: Order these as makes sense <> #+end_src ** Types #+name: grp__types #+begin_src js :noweb yes <> <> <> <> // 'TypeExpressions' are limited to constant integers, variables, and basic numeric binary operators; they are a special type that is allowed in the length position of an array (and some other limited places). // Using 'expr' in-place of 'expression' so-as-to- not conflate with _real_ expressions. <> #+end_src ** Patterns #+name: grp__patterns #+begin_src js :noweb yes <> <> <> #+end_src ** Literals #+name: grp__literals #+begin_src js :noweb yes <> <> <> <> <> <> <> <> <> <> <> <> <> #+end_src * Tangle Custom tangle logic for this file. The goal is to be able to refer to tree-sitter DSL snippets by a constant name which can be easily jumped to, and also to reduce the headache of tedious renaming as things change when using a strategy like this where code snippets may be located far apart in this org document. In all cases a src-block must have a =#+name= associated which is used to refer to it. The substituted name when tangled may be specified by adding a =:rule= header to the associated src-block. Inserts the name of the src-block it literally appears in. #+name: self #+begin_src emacs-lisp (when-let* ((block-src-info (org-babel-lob--src-info block_name)) (block-name (org-element-property :name (org-element-at-point (nth 5 block-src-info)))) (name (or (cdr (assoc :rule (nth 2 block-src-info))) block-name))) (format "$.%s" name)) #+end_src Inserts the name of given src-block parameter =block_name= refers to. #+name: r #+begin_src emacs-lisp :var block_name="" ;; TODO: Format an error (or better yet elisp call `error` if no block-src-info found) (when-let* ((block-src-info (org-babel-lob--src-info block_name)) ;; TODO: block_name fallback here error prone. (rule-name (or (cdr (assoc :rule (nth 2 block-src-info))) block_name))) (format "$.%s" rule-name)) #+end_src Inserts the contents of given src-block parameter =block_name= with it's tangled content name, argument, and org back-links generated. #+name: n #+begin_src emacs-lisp :var block_name="" ;; TODO: Use format-spec instead to get $ as ($) or just use a js formatter after tangling. (when-let* ((block-src-info (org-babel-lob--src-info block_name)) ;; (block-src (cadr block-src-info)) (block-src (org-babel-expand-noweb-references block-src-info)) (rule-arg (or (cdr (assoc :arg (nth 2 block-src-info))) "_")) (rule-name (or (cdr (assoc :rule (nth 2 block-src-info))) block_name)) (buff-name (buffer-name))) (format "\ \n// [[file:%s::%s]] %s: %s => %s," buff-name block_name rule-name rule-arg block-src)) #+end_src Executes =n= for each element of given list of src-block =block_name=\s. #+name: c #+begin_src emacs-lisp :var block_names="" (let (res) (dolist (block_name block_names res) ;; (message "=====> processing: %s / %s" block_name (stringp block_name)) ;; (message "==========> %s" (org-babel-lob--src-info block_name)) ;; (message "===> %s" (concat "$\"" block_name "\"")) ;; (setq res (concat res "\ny" (org-sbe "n" (block_name (concat "$\"" block_name "\""))))) ;; (setq res (concat res "\ny" (org-sbe "n" (block_name $"attribute")))) ;; pp-macroexpand: (org-sbe "n" (block_name $"attribute")) (setq res (concat res (org-babel-execute-src-block nil (list "emacs-lisp" "results" `((:var . ,(format "results=n[](block_name=\"%s\")" block_name)))) '((:results . "none"))))) )) #+end_src #+name: lit #+begin_src emacs-lisp :var literal="" (format "\"%s\"" literal) #+end_src