npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

semantic-fn

v1.2.0

Published

Convert human readable text to executable JS

Downloads

6

Readme

semantic-fn

Convert human readable text to executable JS. Assuming we have a function with a given set of parameters, semantic-fn provides a ways to describe the body of that function in words.

const fnDescriptor = 'person.name.length greater than 5';

// Running the fnDescriptor through semantic-fn should
// return a function that does the following.
function checkNameLength(person) {
  return person['name']['length'] > 5;
}

Why?

Converting a string to an executable function body might seem interesting, but does it really have any practical use? One potential challenge that could be addressed by a tool like semantic-fn is trying to send execution information via JSON over an API request. The simplest way to do something like that would be to just define the function as a string, send it via JSON and use the browser's eval logic to actually execute it. While that may work, it's considered a best practice to avoid eval because it opens your application up to unwanted security concerns.

This is where a tool like semantic-fn comes into play. By defining a strict set of rules for the function descriptor string, we can allow for function logic to be safely transmitted via JSON without the same kinds of security concerns that plague eval.

Rules

  • Functions which semantic-fn return are pure
  • Strict variable access from a predefined "global" state, function parameters or local variables
  • Can only execute predefined functions, such as the string function trim

How?

semantic-fn is a JS parser with a strict Context-Free Grammar that guarantees that only valid function descriptor strings are executed. If invalid syntax is used, semantic-fn will return a function that emits undefined (or a predefined default value) when invoked.

Context-Free Grammar

The grammar specified by semantic-fn is designed to be as close to JavaScript as possible. The following expressions and statements are allowed:

  • Literals: Numbers, strings, Booleans, undefined and null.
  • Identifier: A special type of literal which is either a reserved keyword or a variable name.
  • Collections: Standard JS objects and arrays.
    • An object in semantic-fn is prefixed with a % (e.g. %{ property: 'value' })
  • Unary expressions: A prefix ! which performs a logical not and - to negate a number and any MODIFIER which modifies the value after it.
  • Binary expressions: The following infix arithmetic operators (+, -, *, /) and the following logical operators (==, ===, !=, !==, <, <=, >, >=, and, or). These operators behave the same way you would expect them to behave in JavaScript.
  • Parentheses: A way to group other expressions to show desired precedence.
  • Statements: A statement is a series of tokens which can be executed in isolation. semantic-fn opts to return the value of a statement which means there's no need for an explicit return keyword and the last statement in the parsed descriptor string is the return value of the returned function. The following statement types exist:
    • Expression: Any expression followed by a new line which signifies the end of the expression.
    • Declarations: A special statement which provides a way to store state in a local variable which can be reassigned. The variable created adheres to standard JavaScript scoping rules.
    • Block: A series of statements with a unique environment and variable scope.
    • If: A standard if / else statement to allow for conditional logic.

Special Considerations

  • Scope: semantic-fn has three types of scope (global, fnArgs and block). The order of precedence for the scopes is block → fnArgs → global where duplicate variable names in a lower scope will shadow a variable in a higher scope. global and fnArgs are readonly to prevent mutation of external state.

    • global: Passed in using the scope option when building a function. It's an object where the key is the variable name and the value is the, well, value.
    • fnArgs: These are the arguments which are passed into the function when it's executed. The variable names are defined using the argNames option. It's an array of strings where the string is the variable name and the index is the function parameter you want to reference.
    • block: Variables can be defined in the descriptor string that's passed to semantic-fn. Any variables defined in a block will get their own variable scope.
  • Modifiers

    • toString - convert the following expression to a string
    • toBool - convert the following expression to a Boolean

Rules

The grammar rules are defined below. Instead of using something like Backus-Naur form or a variant of that, the following syntax has been implemented. Each rule consists of text, followed by ( → ), followed by a sequence of more text. The last character of each rule is a semicolon ( ; ) which indicates the end of the rule. Each rule may use a pipe ( | ) operator to express a choice. The star ( * ) operator implies 0 or more times and the plus ( + ) operator implies 1 or more times. The Terminals are in double quotes or are uppercase words and nonterminals are lowercase words.

-- Grammar Rules (in ascending order of precedence)
program       → declaration* EOT ;
declaration   → letDecl
                | statement ;
letDecl       → "let" IDENTIFIER ( "=" expression )? "\n" ;
statement     → exprStmt
                | block
                | ifStmt ;
exprStmt      → expression "\n" ;
block         → "{" declaration* "}" ;
ifStmt        → "if" "(" expression ")" statement ( "else" statement )?
                | "if" expression "," "do:" expression ( "\n", "," "else:" expression "\n" ) ;
expression    → assignment ;
assignment    → ( call "." )? IDENTIFIER "=" assignment
                | logic_or ;
logic_or      → logic_and ( "or" logic_and)* ;
logic_and     → equality ( "and" equality)* ;
equality      → comparison ( ( "!=", "!==", "==", "===" ) comparison )* ;
comparison    → term ( ( ">", ">=", "<", "<=" ) term )* ;
term          → factor ( ( "-", "+" ) factor )* ;
factor        → unary ( ( "/", "*" ) unary )* ;
unary         → ( "!", "-", MODIFIER ) unary
                | get ;
get           → primary ( "." IDENTIFIER, "[" ( NUMBER, STRING, IDENTIFIER ) "]" )* ;
primary       → NUMBER
                | STRING
                | IDENTIFIER
                | "true"
                | "false"
                | "undefined"
                | "null"
                | "%{" ( IDENTIFIER ":" expression ","? )* "}"
                | "[" ( expression ","? )* "]"
                | "(" expression ")" ;