#!/usr/bin/env python3 """ Ny parser MVP (Stage 2): Ny -> JSON v0 Grammar (subset): program := stmt* EOF stmt := 'return' expr | 'local' IDENT '=' expr | 'if' expr block ('else' block)? | 'loop' '(' expr ')' block | expr # expression statement block := '{' stmt* '}' expr := logic logic := compare (('&&'|'||') compare)* compare := sum (('=='|'!='|'<'|'>'|'<='|'>=') sum)? sum := term (('+'|'-') term)* term := factor (('*'|'/') factor)* factor := INT | STRING | IDENT call_tail* | '(' expr ')' | 'new' IDENT '(' args? ')' call_tail:= '.' IDENT '(' args? ')' # method | '(' args? ')' # function call args := expr (',' expr)* Outputs JSON v0 compatible with --ny-parser-pipe. """ import sys, re, json class Tok: def __init__(self, kind, val, pos): self.kind, self.val, self.pos = kind, val, pos KEYWORDS = { 'return':'RETURN', 'local':'LOCAL', 'if':'IF', 'else':'ELSE', 'loop':'LOOP', 'new':'NEW' } def lex(s: str): i=0; n=len(s); out=[] def peek(): return s[i] if i=', i) or s.startswith('&&', i) or s.startswith('||', i): out.append(Tok('OP2', s[i:i+2], i)); i+=2; continue if c in '+-*/(){}.,<>=': out.append(Tok(c, c, i)); i+=1; continue if c=='"': j=i+1; buf=[] while j=')) or k in ('<','>'): op = v if k=='OP2' else self.cur().kind self.i+=1 rhs=self.sum(); return {"type":"Compare","op":op,"lhs":lhs,"rhs":rhs} return lhs def sum(self): lhs=self.term() while self.cur().kind in ('+','-'): op=self.cur().kind; self.i+=1 rhs=self.term(); lhs={"type":"Binary","op":op,"lhs":lhs,"rhs":rhs} return lhs def term(self): lhs=self.factor() while self.cur().kind in ('*','/'): op=self.cur().kind; self.i+=1 rhs=self.factor(); lhs={"type":"Binary","op":op,"lhs":lhs,"rhs":rhs} return lhs def factor(self): tok=self.cur() if self.eat('INT'): return {"type":"Int","value":tok.val} if self.eat('STR'): return {"type":"Str","value":tok.val} if self.eat('('): e=self.expr(); self.expect(')'); return e if self.eat('NEW'): t=self.cur(); self.expect('IDENT'); self.expect('(') args=self.args_opt(); self.expect(')') return {"type":"New","class":t.val,"args":args} if self.eat('IDENT'): node={"type":"Var","name":tok.val} # call/methtail while True: if self.eat('('): args=self.args_opt(); self.expect(')') node={"type":"Call","name":tok.val,"args":args} elif self.eat('.'): m=self.cur(); self.expect('IDENT'); self.expect('(') args=self.args_opt(); self.expect(')') node={"type":"Method","recv":node,"method":m.val,"args":args} else: break return node raise SyntaxError(f"factor at {tok.pos}") def args_opt(self): args=[] if self.cur().kind in (')',): return args args.append(self.expr()) while self.eat(','): args.append(self.expr()) return args def main(): if len(sys.argv)<2: print("usage: ny_parser_mvp.py ", file=sys.stderr); sys.exit(1) with open(sys.argv[1],'r',encoding='utf-8') as f: src=f.read() toks=lex(src) prog=P(toks).program() print(json.dumps(prog, ensure_ascii=False)) if __name__=='__main__': main()