module.exports = grammar({ name: "dfasm", word: ($) => $.identifier, rules: { program: ($) => repeat($._statement), _statement: ($) => choice($.func_def, $.inst_def, $.strong_edge, $.weak_edge, $.plain_edge, $.data_def, $.pragma, $.location_dir), // ===== Function definition ===== // $name |> { body } func_def: ($) => prec(2, seq($.func_ref, $.flow_out, "{", repeat($._statement), "}")), // ===== Instruction definition (named node) ===== // &label <| opcode [, arg ...] inst_def: ($) => prec(2, seq($.qualified_ref, $.flow_in, $.opcode, repeat(seq(",", $._argument)))), // ===== Strong inline edge (internal route, anonymous node) ===== // opcode input [, input ...] |> output [, output ...] strong_edge: ($) => prec(2, seq($.opcode, $._argument, repeat(seq(",", $._argument)), $.flow_out, $.ref_list)), // ===== Weak inline edge (token output, anonymous node) ===== // output [, output ...] opcode <| input [, input ...] weak_edge: ($) => prec(2, seq($.ref_list, $.opcode, $.flow_in, $._argument, repeat(seq(",", $._argument)))), // ===== Plain edge (wiring between named nodes) ===== // source |> dest [, dest ...] plain_edge: ($) => prec(2, seq($.qualified_ref, $.flow_out, $.ref_list)), // ===== Data / initialisation ===== // ref = value | ref = #macro args data_def: ($) => prec(2, seq($.qualified_ref, "=", choice($.macro_call, $.value_list))), // ===== Pragma (system configuration) ===== // @system pe=4, sm=1, iram=128, ctx=2 pragma: ($) => prec(2, seq("@system", $.system_param, repeat(seq(",", $.system_param)))), system_param: ($) => seq(field("name", $.identifier), "=", field("value", $.number_literal)), // ===== Location directive (bare qualified ref, no operator) ===== // Sets location context for subsequent definitions. location_dir: ($) => prec(-1, $.qualified_ref), // ===== Reference list ===== ref_list: ($) => seq($.qualified_ref, repeat(seq(",", $.qualified_ref))), // ===== Qualified references ===== // Qualifier chain: max one placement (|ident) and one port (:spec). // @name — node reference // &name — local label reference // $name — function / subgraph reference // Chaining: @sum|pe0:L (placement + port) qualified_ref: ($) => seq( field("ref", choice($.node_ref, $.label_ref, $.func_ref)), field("placement", optional($.placement)), field("port", optional($.port)), ), node_ref: ($) => seq("@", field("name", $.identifier)), label_ref: ($) => seq("&", field("name", $.identifier)), func_ref: ($) => seq("$", field("name", $.identifier)), placement: ($) => seq(field("separator", token.immediate("|")), field("name", $.identifier)), port: ($) => seq(field("separator", token.immediate(":")), field("name", choice($.identifier, $.number_literal))), // ===== Arguments ===== // An argument is a value, a qualified ref, or a named key=value pair. _argument: ($) => choice($.named_arg, $._positional_arg), named_arg: ($) => seq(field("key", $.identifier), "=", field("value", $._positional_arg)), _positional_arg: ($) => choice($._value, $.qualified_ref), // ===== Values / Literals ===== _value: ($) => choice($.number_literal, $.char_literal, $.string_literal, $.raw_string_literal, $.byte_string_literal), value_list: ($) => seq($._value, repeat(seq(",", $._value))), number_literal: ($) => choice($.hex_literal, $.dec_literal), hex_literal: ($) => token(seq("0x", /[0-9a-fA-F]+/)), dec_literal: ($) => /[0-9]+/, char_literal: ($) => seq("'", choice($.escape_sequence, /[^'\\]/), "'"), string_literal: ($) => seq('"', repeat(choice($.escape_sequence, /[^"\\]+/)), '"'), raw_string_literal: ($) => seq('r"', /[^"]*/, '"'), byte_string_literal: ($) => seq('b"', repeat(choice($.escape_sequence, /[^"\\]+/)), '"'), escape_sequence: ($) => /\\([ntr0\\'"]|x[0-9a-fA-F]{2})/, // ===== Macros ===== // #name arg [arg ...] — expanded in a later pass, not during parsing. // Only $._value arguments allowed; qualified_ref removed to prevent greedy consumption macro_call: ($) => prec.right(seq("#", field("name", $.identifier), repeat($._value))), // ===== Opcodes ===== // Exhaustive keyword terminal. opcode: ($) => choice( // Arithmetic operations "add", "sub", "inc", "dec", "shiftl", "shiftr", "ashiftr", // Logic operations "and", "or", "xor", "not", "eq", "lt", "lte", "gt", "gte", // Routing/branch operations "breq", "brgt", "brge", "brof", "brty", "sweq", "swgt", "swge", "swof", "swty", "gate", "sel", "merge", "pass", "const", "free_ctx", "change_tag", "extract_tag", // Memory operations "read", "write", "clear", "alloc", "exec", "free", "rd_inc", "rd_dec", "cmp_sw", // Configuration operations "load_inst", ), // ===== Flow operators ===== flow_in: ($) => "<|", flow_out: ($) => "|>", // ===== Base tokens ===== identifier: ($) => /[a-zA-Z_][a-zA-Z0-9_]*/, comment: ($) => token(seq(";", /[^\n]*/)), }, extras: ($) => [/\s/, $.comment], });