UNPKG

@satachito/jp-formula

Version:
1,084 lines (1,028 loc) 24.9 kB
const stack = [] class Context { constructor( dict, next = null ) { this.dict = dict this.next = next } } class SliP { constructor( _ ) { this._ = _ } string() { return `${this._}` } } const Round = _ => { if ( _ === 1 ) return 1 const roundPrecision = ( () => { try { return Number( ROUND_PRECISION.value ) } catch { return 15 } } )() if ( _ < 1 ) { const coef = Math.pow( 10, roundPrecision ) return Math.round( _ * coef ) / coef // const $ = Math.round( _ * coef ) / coef // console.log( _, $ ) // return $ } else { const coef = Math.pow( 10, roundPrecision - Math.ceil( Math.log10( _ ) ) ) return Math.round( _ * coef ) / coef // console.log( // _ // , Math.log10( _ ) // , Math.ceil( Math.log10( _ ) ) // , roundPrecision - Math.ceil( Math.log10( _ ) ) // , Math.pow( 10, roundPrecision - Math.ceil( Math.log10( _ ) ) ) // , coef // , Math.round( _ * coef ) // , Math.round( _ * coef ) / coef // ) // const $ = Math.round( _ * coef ) / coef // console.log( _, $ ) // return $ } } class Numeric extends SliP { string() { switch ( this._ ) { case Infinity : return '∞' case -Infinity : return '-∞' default : // return this._ return this._ === 0 ? 0 : this._ < 0 ? - Round( - this._ ) : Round( this._ ) } } } class Literal extends SliP { constructor( _, hint ) { super( _ ) this.hint = hint ? hint : '"' } string() { return this.hint + this._ + this.hint } } class Dict extends SliP { string() { let $ = '{' let initChar = '' Object.entries( this._ ).forEach( ( [ k, v ] ) => ( $ = $ + initChar + '\t' + k + '\t: ' + v.string() + '\n' , initChar = ',' ) ) return $ + '}' } } class Name extends SliP { string() { return this._ } } class _Func extends SliP { constructor( _, label ) { super( _ ) this.label = label } string() { return this.label } // string() { return this.label + '/' + this.constructor.name } } class Primitive extends _Func { } class Prefix extends _Func { } class Unary extends _Func { } class Infix extends _Func { constructor( _, label, priority ) { super( _, label ) this.priority = priority } } class _List extends SliP { string() { return this._.map( _ => _.string() ).join( ' ' ) } } class List extends _List { string() { const $ = `[ ${super.string()} ]` return this.matrix ? this.matrix > 0 ? $ + `(${this.matrix},${this._.length/this.matrix})` : $ + `(${this._.length/-this.matrix},${-this.matrix})` : $ } } class Parallel extends _List { string() { return ${super.string()} »` } } class Procedure extends _List { string() { return `{ ${super.string()} }` } } class Sentence extends _List { string() { return `( ${super.string()} )` } } const _EvalSentence = ( c, _ ) => { switch ( _.length ) { case 0: throw 'No operands' case 1: return Eval( c, _[ 0 ] ) default: const infixEntries = _.map( ( v, k ) => [ k, v ] ).filter( ( [ k, v ] ) => v instanceof Infix ) if ( infixEntries.length ) { let $ = infixEntries[ 0 ] infixEntries.slice( 1 ).forEach( _ => _[ 1 ].priority >= $[ 1 ].priority && ( $ = _ ) ) const [ index, infix ] = $ return infix._( c , _EvalSentence( c, _.slice( 0, index ) ) , _EvalSentence( c, _.slice( index + 1 ) ) ) } else { let index = _.length - 1 _ = _.slice() while ( index-- ) { let $ = _[ index ] $ instanceof Prefix || ( $ = Eval( c, $ ) ) $ instanceof Prefix && _.splice( index, 2, $._( c, _[ index + 1 ] ) ) } if ( _.length === 1 ) return _[ 0 ] const $ = _.map( _ => Eval( c, _ ) ) if ( $.every( _ => _ instanceof Numeric ) ) return new Numeric( $.reduce( ( $, _ ) => $ * _._, 1 ) ) } break } throw 'Syntax error' } export const Eval = ( c, _ ) => { try { switch ( _.constructor.name ) { case 'Name': while ( c ) { const $ = c.dict[ _._ ] if ( $ !== void 0 ) return $ instanceof Primitive ? $._( c ) : $ c = c.next } throw 'Undefined' case 'Primitive': return _._( c ) case 'Parallel': return new List( _._.map( _ => Eval( c, _ ) ) ) case 'Procedure': { c = new Context( {}, c ) return new List( _._.map( _ => Eval( c, _ ) ) ) } case 'Sentence': return _EvalSentence( c, _._ ) default: return _ } } catch ( ex ) { let $ = 'Evaluating: ' + _.string() if ( Array.isArray( ex ) ) { ex.push( $ ) throw ex } throw [ ex, $ ] } } //////////////////////////////////////////////////////////////// // Builtins //////////////////////////////////////////////////////////////// const T = new SliP( 'T' ) const Nil = new List( [] ) const _IsT = _ => !( _ instanceof _List ) || _._.length const _IsNil = _ => _ instanceof _List && !_._.length const _EQ = ( p, q ) => { if ( p instanceof Numeric && q instanceof Numeric ) return p._ === q._ if ( p instanceof Literal && q instanceof Literal ) return p._ === q._ if ( p instanceof Name && q instanceof Name ) return p._ === q._ if ( p instanceof _List && q instanceof _List ) { if ( p.constructor.name !== q.constructor.name ) return false if ( p._.length !== q._.length ) return false for ( let i = 0; i < p._.length; i++ ) if ( !_EQ( p._[ i ], q._[ i ] ) ) return false return true } return p === q } const Plus = ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return new Numeric ( l._ + r._ ) if ( l instanceof Literal && r instanceof Literal ) return new Literal ( l._ + r._ ) if ( l instanceof List && r instanceof List ) return new List ( [ ...l._, ...r._ ] ) if ( l instanceof Parallel && r instanceof Parallel ) return new Parallel ( [ ...l._, ...r._ ] ) if ( l instanceof Procedure && r instanceof Procedure ) return new Procedure ( [ ...l._, ...r._ ] ) if ( l instanceof Sentence && r instanceof Sentence ) return new Sentence ( [ ...l._, ...r._ ] ) throw 'Illegal type combination' } const Minus = ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return new Numeric( l._ - r._ ) throw 'Illegal type combination' } const Builtins = [ new Primitive( c => { if ( stack.length ) return stack[ 0 ] throw 'Stack underflow' } , '@' ) , new Primitive( c => new List( stack.slice() ) , '@@' ) , new Primitive( c => new Dict( c.dict ) , '¤' ) , new Prefix( ( c, _ ) => _ , '\'' ) , new Prefix( ( c, _ ) => { throw _.string() } , '¡' ) , new Prefix( ( c, _ ) => { const v = Eval( c, _ ) if ( v instanceof Numeric ) return new Numeric( ~v._ ) throw 'Illegal operand type' } , '~' ) , new Prefix( ( c, _ ) => _IsT( Eval( c, _ ) ) ? Nil : T , '¬' ) , new Prefix( ( c, _ ) => Eval( c, Eval( c, _ ) ) , '!' ) , new Prefix( ( c, _ ) => _ instanceof Literal ? _ : new Literal( _.string() ) , '¶' ) , new Unary( ( c, _ ) => { if ( _ instanceof _List ) return new Numeric( _._.length ) if ( _ instanceof Literal ) return new Numeric( _._.length ) throw 'Illegal operand type' } , '#' ) , new Unary( ( c, _ ) => { if ( _ instanceof _List && _._.length ) { switch ( _.constructor.name ) { case '_List' : return new _List ( _._.slice( 1 ) ) case 'List' : return new List ( _._.slice( 1 ) ) case 'Parallel' : return new Parallel ( _._.slice( 1 ) ) case 'Sentence' : return new Sentence ( _._.slice( 1 ) ) case 'Procedure': return new Procedure ( _._.slice( 1 ) ) } } throw 'Illegal operand type' } , '*' ) , new Unary( ( c, _ ) => { if ( ! ( _ instanceof _List ) ) throw 'Illegal operand type' const length = _._.length if ( !length ) throw 'No elements' return _._[ length - 1 ] } , '$' ) , new Unary( ( c, _ ) => ( console.log( _.string() ), _ ) , '.' ) , new Unary( ( c, _ ) => ( console.error( _.string() ), _ ) , '¦' ) /* : 10 Apply 2 x [ 3, 4 ]:1 2 x 4 × 20 Multi/Div 2 + 3 x 4 2 + 12 + 30 Plus/Minus 2 | 3 + 4 2 | 7 | 40 Binary 2 , 3 | 4 2 < 7 , 50 Cons [ 2 3 ] == 2 , [ 3 ] [ 2 3 ] == [ 2, 3 ] < 60 Compare 1 || 2 < 3 1 || T ∈ 60 Member 1 || 2 ∈ [ 1, 2, 3 ] 1 || T || 70 Logical 'a = [ 2 ] || T a = T ? 80 IfElse = 90 Define */ , new Infix( ( c, l, r ) => Eval( new Context( l._, c ), r ) , '§' , 100 ) , new Infix( ( c, l, r ) => { if ( l instanceof Name ) return c.dict[ l._ ] = r throw 'Only name can be assigned.' } , '=' , 90 ) , new Infix( ( c, l, r ) => { if ( r instanceof List && r._.length == 2 ) return Eval( c, r._[ _IsT( l ) ? 0 : 1 ] ) throw 'Right operand must be two element List.' } , '?' , 80 ) , new Infix( ( c, l, r ) => _IsT( l ) ? Eval( c, r ) : Nil , '¿' , 80 ) , new Infix( ( c, l, r ) => _IsT( l ) & _IsT( r ) ? T : Nil , '&&' , 70 ) , new Infix( ( c, l, r ) => _IsT( l ) | _IsT( r ) ? T : Nil , '||' , 70 ) , new Infix( ( c, l, r ) => _IsT( l ) ^ _IsT( r ) ? T : Nil , '^^' , 70 ) , new Infix( ( c, l, r ) => { if ( r instanceof _List ) return r._.filter( _ => _EQ( _, l ) ).length ? T : Nil throw 'Right operand must be List' } , '∈' , 60 ) , new Infix( ( c, l, r ) => { if ( l instanceof _List ) return l._.filter( _ => _EQ( _, r ) ).length ? T : Nil throw 'Left operand must be List' } , '∋' , 60 ) , new Infix( ( c, l, r ) => _EQ( l, r ) ? T : Nil , '==' , 60 ) , new Infix( ( c, l, r ) => _EQ( l, r ) ? Nil : T , '<>' , 60 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return l._ < r._ ? T : Nil throw 'Illegal operand type' } , '<' , 60 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return l._ > r._ ? T : Nil throw 'Illegal operand type' } , '>' , 60 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return l._ <= r._ ? T : Nil throw 'Illegal operand type' } , '<=' , 60 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return l._ >= r._ ? T : Nil throw 'Illegal operand type' } , '>=' , 60 ) , new Infix( ( c, l, r ) => { if ( r instanceof _List ) { switch ( r.constructor.name ) { case '_List' : return new _List ( [ l, ...r._ ] ) case 'List' : return new List ( [ l, ...r._ ] ) case 'Parallel' : return new Parallel ( [ l, ...r._ ] ) case 'Sentence' : return new Sentence ( [ l, ...r._ ] ) case 'Procedure' : return new Procedure ( [ l, ...r._ ] ) default : throw 'internal error' } } throw 'Right operand must be List' } , ',' , 50 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return new Numeric( l._ & r._ ) throw 'Illegal operand type' } , '&' , 40 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return new Numeric( l._ | r._ ) throw 'Illegal operand type' } , '|' , 40 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return new Numeric( l._ ^ r._ ) throw 'Illegal operand type' } , '^' , 40 ) , new Infix( Plus , '+' , 30 ) , new Infix( Minus , '-' , 30 ) , new Infix( ( c, l, r ) => { if ( l instanceof List && r instanceof List ) { const [ numRowL, numColL ] = l.matrix ? l.matrix > 0 ? [ l.matrix, l._.length / l.matrix ] : [ l._.length / -l.matrix, -l.matrix ] : [ 1, l._.length ] const [ numRowR, numColR ] = r.matrix ? r.matrix > 0 ? [ r.matrix, r._.length / r.matrix ] : [ r._.length / -r.matrix, -r.matrix ] : [ r._.length, 1 ] if ( numColL !== numRowR ) throw 'Matrix size error' if ( numRowL === 1 && numColR === 1 ) { let $ = 0 for ( let _ = 0; _ < numColL; _++ ) $ += l._[ _ ]._ * r._[ _ ]._ return new Numeric( $ ) } else { const $ = new Array( numRowL * numColR ) for ( let row = 0; row < numRowL; row++ ) { const _$ = row * numColR for ( let col = 0; col < numColR; col++ ) { let _ = 0 for ( let k = 0; k < numColL; k++ ) _ += l._[ row * numColL + k ]._ * r._[ col + k * numColR ]._ $[ _$ + col ] = _ } } const v = new List( $.map( _ => new Numeric( _ ) ) ) v.matrix = numRowL return v } } throw 'Operands must be List' } , '·' , 20 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return new Numeric( l._ * r._ ) throw 'Illegal operand type' } , '×' , 20 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return new Numeric( l._ / r._ ) throw 'Illegal operand type' } , '÷' , 20 ) , new Infix( ( c, l, r ) => { if ( l instanceof Numeric && r instanceof Numeric ) return new Numeric( l._ % r._ ) throw 'Illegal operand type' } , '%' , 20 ) , new Infix( ( c, l, r ) => { switch ( r.constructor.name ) { case 'Numeric' : { if ( ! ( l instanceof _List ) ) throw 'Left operand must be List' let v = l._[ r._ ] return v ? v : Nil } case 'Name' : case 'Literal' : { if ( ! ( l instanceof Dict ) ) throw 'Left operand must be Dict' let v = l._[ r._ ] return v ? v : Nil } case 'Unary' : return r._( c, l ) default: { stack.unshift( l ) const v = Eval( c, r ) stack.shift() return v } } } , ':' , 10 ) ] //////////////////////////////////////////////////////////////// // Reader //////////////////////////////////////////////////////////////// const BuiltinLabels = Builtins.map( _ => _.label ) const BuiltinLabel0s = BuiltinLabels.map( _ => _[ 0 ] ) const SoloChars = [ 'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο', 'Π', 'Ρ', 'Σ', 'Τ', 'Υ', 'Φ', 'Χ', 'Ψ', 'Ω' , 'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'ς', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω' , '∞' ] const NAME_BREAKING_CHARACTERS = [ ...SoloChars , '!' // 21 Eval , '"' // 22 StringLiteral , '#' // 23 Num , '$' // 24 Last , '%' // 25 Reminder , '&' // 26 AND , '\'' // 27 Quote , '(' // 28 Open sentence , ')' // 29 Close sentence , '*' // 2A CDR , '+' // 2B Plus , ',' // 2C CONS , '-' // 2D Minus , '.' // 2E Console.log, returns argument , '/' // 2F , ':' // 3A Apply , ';' // 3B Dummy sentence terminator for Sugared syntax , '<' // 3C COMPARATOR , '=' // 3D COMPARATOR , '>' // 3E COMPARATOR , '?' // 3F if L is non-Nil evaluate R[ 0 ] otherwise evaluate R[ 1 ] , '@' // 40 Stack top, Stack , '[' // 5B Open list , ']' // 5D Close list , '^' // 5E Exclusive logical OR , '`' // 60 String Literal , '{' // 7B Open procedure , '|' // 7C OR , '}' // 7D Close procedure , '~' // 7E Bit flip , '¡' // A1 Throw , '¤' // A4 Dict , '¦' // A6 Console.error, returns argument , '§' // A7 Evaluate R in context of L , '«' // AB Open parallel , '¬' // AC NOT , '±' // B1 , 'µ' // B5 , '¶' // B6 Stringify , '·' // B7 Dot , '»' // BB Close parallel , '¿' // BF If L is non-NIL evaluate R otherwise Nil , '×' // D7 MUL , '÷' // F7 DIV , '∈' // 2208 Member , '∋' // 220B Includes , '⊂' // , '⊃' // , '∩' // , '∪' // , '∅' // ] const ReadNumber = ( r, _ ) => { let v = _ let readDot = false while ( r.Avail() ) { const _ = r.Peek() if ( !_.match( /[\d.]/ ) ) break r.Forward() if ( _ === '.' ) { if ( readDot ) break else readDot = true } v += _ } return new Numeric( Number( v ) ) } const ReadName = ( r, _ ) => { if ( SoloChars.includes( _ ) ) return new Name( _ ) let escaped = _ === '\\' let v = escaped ? '' : _ while ( r.Avail() ) { const _ = r.Peek() if ( escaped ) { r.Forward() escaped = false switch ( _ ) { case '0' : v += '\0' ; break case 'f' : v += '\f' ; break case 'n' : v += '\n' ; break case 'r' : v += '\r' ; break case 't' : v += '\t' ; break case 'v' : v += '\v' ; break default : v += _ ; break } } else { if ( NAME_BREAKING_CHARACTERS.includes( _ ) ) break r.Forward() if ( _.match( /\s/ ) ) break // [ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] switch ( _ ) { case '\\' : escaped = true ; break default : v += _ ; break } } } return new Name( v ) } const ReadLiteral = ( r, terminator ) => { let escaped = false let v = '' while ( r.Avail() ) { const _ = r.Read() if ( escaped ) { escaped = false switch ( _ ) { case '0' : v += '\0' ; break case 'f' : v += '\f' ; break case 'n' : v += '\n' ; break case 'r' : v += '\r' ; break case 't' : v += '\t' ; break case 'v' : v += '\v' ; break default : v += _ ; break } } else { switch ( _ ) { case terminator : return new Literal( v, terminator ) case '\\' : escaped = true ; break default : v += _ ; break } } } throw `Unterminated string: ${v}` } const ReadList = ( r, terminator ) => { const $ = [] while ( true ) { const slip = Read( r, terminator ) if ( slip === void 0 ) break // Read terminator if ( slip === null ) throw $.reduce( ( $, _ ) => $ + ' ' + _.string(), 'Open list: ' ) // + $.map( _ => _.string() ) $.push( slip ) } const ModP = _ => $[ _ + 1 ] instanceof Numeric ? $.splice( _, 1 ) : $[ _ ] = new Prefix( ( c, _ ) => new Numeric( +Eval( c, _ )._ ), '+' ) const ModM = _ => $[ _ + 1 ] instanceof Numeric ? $.splice( _, 2, new Numeric( -$[ _ + 1 ]._ ) ) : $[ _ ] = new Prefix( ( c, _ ) => new Numeric( -Eval( c, _ )._ ), '-' ) if ( $.length > 1 ) { switch ( $[ 0 ]._ ) { case Plus : ModP( 0 ) ; break case Minus : ModM( 0 ) ; break } } { let i = $.length - 1 while ( i-- > 1 ) { const _ = $[ i - 1 ] if ( _ instanceof Infix ) { switch ( $[ i ]._ ) { case Plus : ModP( i ) ; break case Minus : ModM( i ) ; break } } } } return $ } const BuiltinDict = Object.fromEntries( Builtins.map( _ => [ _.label, _ ] ) ) export const Read = ( r, terminator ) => { while ( r.Avail() ) { let _ = r.Read() if ( _.match( /\d/ ) ) return ReadNumber( r, _ ) if ( _.match( /\s/ ) ) continue // [ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] switch ( _ ) { case terminator : return void 0 // Must be before close brace case ']' : case '}' : case ')' : case '»' : return null // throw `Unexpected brace closer: ${_}` case '[' : return new List ( ReadList( r, ']' ) ) case '(' : return new Sentence ( ReadList( r, ')' ) ) case '{' : return new Procedure ( ReadList( r, '}' ) ) case '«' : return new Parallel ( ReadList( r, '»' ) ) case '"' : return ReadLiteral( r, _ ) case '`' : return ReadLiteral( r, _ ) default : if ( BuiltinLabel0s.includes( _ ) ) { const c2 = r.Peek() return BuiltinLabels.includes( _ + c2 ) ? ( r.Forward() , BuiltinDict[ _ + c2 ] ) : BuiltinDict[ _ ] } else { return ReadName( r, _ ) } } } return null } //////////////////////////////////////////////////////////////// // REPL //////////////////////////////////////////////////////////////// export class StringReader { constructor( $ ) { this.$ = $ this._ = 0 } Avail() { return this._ < this.$.length } Read() { return this.$[ this._++ ] } Peek() { return this.$[ this._ ] } Forward() { this._++ } Backward() { --this._ } } const SliP_JObject = _ => { switch ( typeof _ ) { case 'number' : return new Numeric( _ ) case 'string' : return new Literal( _ ) case 'object' : if ( _ === null ) return new List( [] ) switch ( _.constructor ) { case Array: return new List( _.map( _ => SliP_JObject( _ ) ) ) case Object: return new Dict( Object.fromEntries( Object.entries( _ ).map( ( [ k, v ] ) => [ k, SliP_JObject( v ) ] ) ) ) case String: return new Literal( _ ) } } } const JObject_SliP = _ => { switch ( _.constructor ) { case Numeric: case Literal: return _._ case List: return _._.length ? _._.map( _ => JObject_SliP( _ ) ) : null case Dict: return Object.fromEntries( Object.entries( _._ ).map( ( [ k, v ] ) => [ k, JObject_SliP( v ) ] ) ) } throw _.string() + ' can not be converted to JSON' } const NEval = ( c, _ ) => { if ( _ === void 0 ) throw 'Insufficient argument' const $ = Eval( c, _ ) if ( $.constructor !== Numeric ) throw 'Not a number' if ( Number.isNaN( $._ ) ) throw 'Argument is NaN' return $ } export const NewContext = () => new Context( {} , new Context( [ new Unary( ( c, _ ) => SliP_JObject( JSON.parse( _._ ) ) , 'byJSON' ) , new Unary( ( c, _ ) => new Literal( JSON.stringify( JObject_SliP( _ ) ) ) , 'toJSON' ) , new Unary( ( c, _ ) => new Numeric( parseInt( _._[ 0 ]._, _._[ 1 ]._ ) ) , 'int' ) , new Unary( ( c, _ ) => new Literal( _._[ 0 ]._.toString( _._[ 1 ]._ ) ) , 'string' ) // MATH EXTENSION , new Prefix( ( c, _ ) => new Numeric( Math.abs( NEval( c, _ )._ ) ) , 'abs' ) , new Prefix( ( c, _ ) => new Numeric( Math.acos( NEval( c, _ )._ ) ) , 'acos' ) , new Prefix( ( c, _ ) => new Numeric( Math.acosh( NEval( c, _ )._ ) ) , 'acosh' ) , new Prefix( ( c, _ ) => new Numeric( Math.asin( NEval( c, _ )._ ) ) , 'asin' ) , new Prefix( ( c, _ ) => new Numeric( Math.asinh( NEval( c, _ )._ ) ) , 'asinh' ) , new Prefix( ( c, _ ) => new Numeric( Math.atan( NEval( c, _ )._ ) ) , 'atan' ) , new Prefix( ( c, _ ) => new Numeric( Math.atanh( NEval( c, _ )._ ) ) , 'atanh' ) , new Prefix( ( c, _ ) => new Numeric( Math.atan2( NEval( c, _._[ 0 ] )._, NEval( c, _._[ 1 ] )._ ) ) , 'atan2' ) , new Prefix( ( c, _ ) => new Numeric( Math.cbrt( NEval( c, _ )._ ) ) , 'cbrt' ) , new Prefix( ( c, _ ) => new Numeric( Math.ceil( NEval( c, _ )._ ) ) , 'ceil' ) , new Prefix( ( c, _ ) => new Numeric( Math.cos( NEval( c, _ )._ ) ) , 'cos' ) , new Prefix( ( c, _ ) => new Numeric( Math.cosh( NEval( c, _ )._ ) ) , 'cosh' ) , new Prefix( ( c, _ ) => new Numeric( Math.exp( NEval( c, _ )._ ) ) , 'exp' ) , new Prefix( ( c, _ ) => new Numeric( Math.floor( NEval( c, _ )._ ) ) , 'floor' ) , new Prefix( ( c, _ ) => new Numeric( Math.hypot( ...Eval( c, _ )._.map( _ => NEval( c, _ )._ ) ) ) , 'hypot' ) , new Prefix( ( c, _ ) => new Numeric( Math.log( NEval( c, _ )._ ) ) , 'log' ) , new Prefix( ( c, _ ) => new Numeric( Math.log10( NEval( c, _ )._ ) ) , 'log10' ) , new Prefix( ( c, _ ) => new Numeric( Math.log2( NEval( c, _ )._ ) ) , 'log2' ) , new Prefix( ( c, _ ) => new Numeric( Math.max( ...Eval( c, _ )._.map( _ => NEval( c, _ )._ ) ) ) , 'max' ) , new Prefix( ( c, _ ) => new Numeric( Math.min( ...Eval( c, _ )._.map( _ => NEval( c, _ )._ ) ) ) , 'min' ) , new Prefix( ( c, _ ) => new Numeric( Math.pow( NEval( c, _._[ 0 ] )._, NEval( c, _._[ 1 ] )._ ) ) , 'pow' ) , new Primitive( c => new Numeric( Math.random() ) , 'random' ) , new Prefix( ( c, _ ) => new Numeric( Math.round( NEval( c, _ )._ ) ) , 'round' ) , new Prefix( ( c, _ ) => new Numeric( Math.sign( NEval( c, _ )._ ) ) , 'sign' ) , new Prefix( ( c, _ ) => new Numeric( Math.sin( NEval( c, _ )._ ) ) , 'sin' ) , new Prefix( ( c, _ ) => new Numeric( Math.sinh( NEval( c, _ )._ ) ) , 'sinh' ) , new Prefix( ( c, _ ) => new Numeric( Math.sqrt( NEval( c, _ )._ ) ) , 'sqrt' ) , new Prefix( ( c, _ ) => new Numeric( Math.tan( NEval( c, _ )._ ) ) , 'tan' ) , new Prefix( ( c, _ ) => new Numeric( Math.tanh( NEval( c, _ )._ ) ) , 'tanh' ) , new Prefix( ( c, _ ) => new Numeric( Math.trunc( NEval( c, _ )._ ) ) , 'trunc' ) , new Unary( ( c, _ ) => { if ( ! _ instanceof List ) throw "matrix's argument must be List." if ( _._.length !== 2 ) throw "matrix's argument length must be 2." const [ list, numeric ] = _._ if ( ! list instanceof List ) throw "matrix's 1st element must be List." if ( ! numeric instanceof Numeric ) throw "matrix's 2nd element must be Numeric." if ( ! Number.isInteger( numeric._ ) ) throw "matrix's 2nd element must be integer." if ( numeric._ === 0 ) throw "matrix's 2nd element must be non zero." if ( ! Number.isInteger( list._.length / numeric._ ) ) throw "matrix's size unmatch." const $ = new List( list._.slice() ) $.matrix = numeric._ return $ } , 'matrix' ) // TODO element ].reduce( ( $, _ ) => ( $[ _.label ] = _, $ ) , { π : new Numeric( Math.PI ) , e : new Numeric( Math.E ) , '∞' : new Numeric( Infinity ) } ) ) ) export const Sugared = _ => new Sentence( ReadList( new StringReader( _ + ')' ), ')' ) )