no-context / moo

Optimised tokenizer/lexer generator! šŸ„ Uses /y for performance. Moo.
BSD 3-Clause "New" or "Revised" License
817 stars 65 forks source link

pre-inline 'reGroups' to prevent uglify from fucking inlining it up #120

Closed Zemnmez closed 2 years ago

nathan commented 5 years ago

Out of idle curiosity, how exactly does uglify transgress? (Perhaps you should file an upstream bug report as well.)

Zemnmez commented 5 years ago

yeah... I just wanted my thing to work, but iā€™ll consider it this weekend. reGroups got inlined, but without correctly changing the variable it takes, so it would throw ā€˜s is undefinedā€™ (s is the only argument it takes)

tjvr commented 5 years ago

That seems like a fairly serious bug in uglify šŸ˜•

nathan commented 5 years ago

reGroups got inlined, but without correctly changing the variable it takes, so it would throw ā€˜s is undefinedā€™ (s is the only argument it takes)

I can't reproduce this. What version of uglify are you using, and how are you invoking it?

~/p/node/moo:master?$ uglifyjs --version
uglify-js 3.4.9
~/p/node/moo:master?$ uglifyjs moo.js --compress --mangle -o moo.min.js
~/p/node/moo:master?$ node
> moo = require('./moo.min');
> lexer = moo.compile({test: /\w+/, ws: {match: /\s+/, lineBreaks: true}})
> lexer.reset('this is a test')
> lexer.next()
{ type: 'test',
  value: 'this',
  text: 'this',
  toString: [Function: h],
  offset: 0,
  lineBreaks: 0,
  line: 1,
  col: 1 }
> lexer.next()
{ type: 'ws',
  value: ' ',
  text: ' ',
  toString: [Function: h],
  offset: 4,
  lineBreaks: 0,
  line: 1,
  col: 5 }
> lexer.next()
{ type: 'test',
  value: 'is',
  text: 'is',
  toString: [Function: h],
  offset: 5,
  lineBreaks: 0,
  line: 1,
  col: 6 }
> ...
Zemnmez commented 5 years ago

I'm just using create-react-app. Bug would only happen when I built for production, which bewildered me. This fixed the issue

Zemnmez commented 5 years ago

here's the code in the file that was using moo.js:

import React from 'react';
import moo from '@zemnmez/moo';

const match = {
  comment: {
    lineBreaks: true,
    match: /#.+/,
    value: v => v.slice(1).trim()
  },

  whitespace: {
    lineBreaks: true,
    match: /\s+/
  },

  ident: {
    match: /[^\n:\-><\[#]+/,
    value: v => v.trim()
  }
}

export const lexer = moo.states({
    main: {
      sender: {...match.ident},
      comment: {...match.comment, pop: true},
      indent: {match: /[\t ]+/},
      whitespace: {...match.whitespace},
      arrow: {match: /<?->?/, push: 'connection.receiver'},
      colon: {match: ':', push: 'connection.receiver'},
      mainError: moo.error,
    },

    'connection': {
      // a ->
      connectionError: moo.error,
    },

    'connection.receiver': {
      // a -> b
      receiver: {...match.ident, push: 'connection.annotation'},
      recieverError: moo.error,
    },

    'connection.annotation': {
      // [HTTP]
      annotation: {
        match: /\[[^\n\]]+?\]/,
        push: 'endline',
        value: v => v.trim().slice(1,-1).trim()
      },

      // # cool
      comment: {...match.comment, push: 'main'},
      whitespace: {...match.whitespace, push: 'main'},
      annotationError: moo.error,
    },

    'endline': {
      // # cool
      comment: {...match.comment, push: 'main'},
      whitespace: {...match.whitespace, push: 'main'},
      horizontalWhitespace: {match: / /},
      endLineError: moo.error,
    }
  })

export default class Lexer extends React.PureComponent {
  lexer = lexer
  render() {
    const {props: {text}, lexer} = this;
    if (!text) return "";
    lexer.reset(text);
    return <div className="lexer">
      {[...lexer].map((token, key) => <Token {...{token, key}}/>)}
    </div>
  }
}

//const Token = ({token: {type, value, text, offset, lineBreaks, line, col}) => <div className="token">
const Token = ({token: {type, ...etc}}) => <div className="token">
  <header>{type}</header>
  <table>
  <tbody>
  {Object.entries(etc).map(([key, value], index) => <tr key={index}>
    <td>{key}</td>
    <td>{JSON.stringify(value)}</td>
  </tr>)}
  </tbody>
  </table>
</div>

I think something that might have confused the compiler is that I'm not using the react component in this file for anything but debugging. I'm using the lexer directly in nearley via a babel macro I patched onto a custom version of nearley.

nathan commented 5 years ago

Please create and share a minimal working example with us. It's very hard to diagnose when we only have some of the code you're using.

attilaaronnagy commented 5 years ago

please accept this fork ... I had exactly the same problem and now I have to use @Zemnmez fork to use moo in production!

@Zemnmez thanks a lot for the fork!!

tjvr commented 2 years ago

I don't believe we ever reproduced this bug; hopefully uglify fixed it themselves!