module.exports = grammar({ name: 'hack_assembly', rules: { source_file: $ => repeat($._item), _item: $ => choice( $.a_instruction, $.c_instruction, $.label_declaration, $.comment, $._whitespace ), // A-instruction: @value or @symbol a_instruction: $ => seq( '@', choice( $.constant, $.symbol ) ), // C-instruction: [dest=]comp[;jump] c_instruction: $ => seq( optional(seq($.dest, '=')), $.comp, optional(seq(';', $.jump)) ), // Label declaration: (SYMBOL) label_declaration: $ => seq( '(', $.symbol, ')' ), // Constants: -32768 to 32767 (16-bit signed) constant: $ => /-?\d+/, // Symbols: letters, digits, _, ., $, : (not starting with digit) symbol: $ => choice( $.predefined_symbol, $.user_symbol ), // Predefined symbols predefined_symbol: $ => choice( // Virtual registers R0-R15 /R(0|1[0-5]|[2-9])/, // Special symbols 'SP', 'LCL', 'ARG', 'THIS', 'THAT', // I/O symbols 'SCREEN', 'KBD' ), // User-defined symbols user_symbol: $ => /[A-Za-z_.$:][A-Za-z0-9_.$:]*/, // Destination field dest: $ => choice( 'M', 'D', 'MD', 'A', 'AM', 'AD', 'AMD' ), // Computation field comp: $ => choice( // Zero and one '0', '1', '-1', // D register operations 'D', '!D', '-D', 'D+1', 'D-1', // A register operations 'A', '!A', '-A', 'A+1', 'A-1', // M register operations 'M', '!M', '-M', 'M+1', 'M-1', // Two operand operations with D and A 'D+A', 'D-A', 'A-D', 'D&A', 'D|A', // Two operand operations with D and M 'D+M', 'D-M', 'M-D', 'D&M', 'D|M' ), // Jump field jump: $ => choice( 'JGT', 'JEQ', 'JGE', 'JLT', 'JNE', 'JLE', 'JMP' ), // Comments comment: $ => token(seq('//', /.*/)), // Whitespace _whitespace: $ => /\s+/ }, extras: $ => [ /\s/, $.comment ] });