# Background

This challenge is based on a personal project I did, UnitDC. This challenge is a super-miniaturized version of UnitDC.

# Challenge

Please write a simple unit-aware calculator that evaluates a sequence of positive integers, units and operators in RPN (postfix) notation, while checking whether the units are compatible with each other.

# Tokens

## Numbers

Numbers are all positive integers in the form "[0-9]+".

## Units

Pops a value off the stack and set the unit of that quantity. For simplicity, units are represented by a single lower-cased letter. All letters are considered base units. Additionally it is guaranteed that the top of stack will have a quantity with no unit when the unit token is encountered.

## Operators

For simplicity there are only three operators, +, * and /. All pop 2 values off the stack and push one back. It is guaranteed that the stack will not underflow.

• For the + operator, you should check whether the two operands are of the same unit, if not you should report error.
• For the * and / operator, you should update the units accordingly (sum up or subtract the exponents of rhs to lhs). All divisions are guaranteed to be whole.

# I/O

You can choose take Input in any of the following forms, from STDIN, function arguments or a memory location:

• A string of tokens, separated by space.
• An array of token strings.

At the end of the evaluation, print to STDOUT or return as value the contents of the stack (top to bottom or bottom to top) in one of the following forms:

• An array of quantities.
• A string concatenation of quantities, separated by space.

where quantity is a string represented by the regex "^\d+([a-z](-?\d+))*$". The notation is simply the concatenation of the number, the letter for each unit and then its exponents. For example "1m2s-3" means $$\1m^2s^{-3}\$$. Valid representations include "1" "1g1" "1m1s-2". "1a0" is INVALID (no zero exponents). Ordering of the units are not important. If an error is encountered during the evaluation, either output '*' and nothing else, or crash, or exit with a non zero code. # Examples I: 1 O: 1 I: 1 s O: 1s1 I: 1 s 2 m + O: * I: 1 m 1 s / 1 m 1 s / + O: 2s-1m1 I: 1 s 2 s * 1 s O: 2s2 1s1 I: 1 s 1 s / O: 1 Note: *1s0 is not acceptable here* I: 1 m 1 s / 2 m 2 s / + O: 2m1s-1 I: 1 s 1 s / 1 + O: 2 I: 1 m 1 m / s S: 1s1 I: 5 s 2 * S: 10s1 I: 5 s 2 + O: *  # Reference Implementation (Rust) This is a small reference implementation I made, this program accepts multiple lines of inputs but you only need to accept one input. Try it online! # Related Similar questions that may be helpful: # Scoring This is code-golf, shortest code wins! • Is 1 m 1 m / s valid? – tsh Commented Mar 1 at 11:08 • Ohhh, yes it should be valid since both values on the stack has no unit, I will update that on the question! thanks for pointing that out Commented Mar 1 at 14:26 • Reference Implementation in Scala, fails on one case. Any help would be appreciated. Attempt This Online! Commented Mar 3 at 11:08 • Your example output 1g does not meet the requirement of regex. Do you mean 1g1 or do you mean \d* in regex? – tsh Commented Mar 5 at 2:59 ## 6 Answers # K (ngn/k), 134124 117 bytes {,//$x,(!y),'.y}.'|(){(,\$[43=*y-*|&/=/h;E;1=C;**x;^[_%;.y].|*'h],(&~b)_b:(y!C;*|(1-3!*y)/h:C#x)2=C),(C:3!"0a"'*y)_x}/


Try it online! Input and output as list of strings, t converts between that and space separated format.

# JavaScript (Node.js), 797 795 bytes

Z=require('util');O=Object;N="length";A=u=>O.fromEntries([...u.matchAll(/([a-zA-Z])(-?\d+)/g)].map(_=> _.slice(1)));B=u=>{s="";e=O.entries(u);for(i=0;i<e[N];i++){s+=e[i][0]+e[i][1]}return s};D=T=>{S=[];U=[];p=/[+*\/]/;for(t of T){if(/\d+/.test(t[0])||/\d+/.test(t[1])&&(t[0]=="-"||t[0]=="+")){S.push(t)}else if(p.test(t)){let[r,l]=[S.pop(),S.pop()];[h,H]=[U.pop(),U.pop()];if(H){f=A(h);g=A(H);m=1;if(t=="+"){if(Z.isDeepStrictEqual(g,f)){n=B(g)}else{return'*'}}if(t=="/"){m=-1}L=O.keys(g);P=O.keys(f);z={};for(i=0;i<L[N];i++){k=L[i];if(k in f){y=+g[k]+m*+f[k];y!=0?(z[k]=y):0}else{z[k]=+g[k]}}for(i=0;i<P[N];i++){k=P[i];if(!(k in z||k in g)){z[k]=m*+f[k]}}n=B(z);n!=""?U.push(n):0}S.push(""+eval(l+t+r))}else{if(t[N] > 1){U.push(t)}else{U.push(t+1)}}}return S.map((E,I)=> E+(U[I]||"")).join(" ")}


Try it online! Input is as an array of token strings passed to D. Output is to STDOUT. There's probably a shorter way, but this is what I've got (and JS is not a golfing language).

• Actually, JS is one of the better languages for code-golf if you're just talking about those which are also used outside of it. Commented Mar 5 at 11:54

# JavaScript (Node.js), 209 bytes

p=>p.map(n=>a.push(y=+n?[+n,...Buffer(36)]:n<0+a.pop()?a.pop().map((o,i)=>(m=y[i],i)?n<'+'?o+m:n<'/'?o-m?E:o:o-m:eval(o+n+m)):(y[parseInt(n,36)]=1,y)),a=[])&&a.map(x=>x.reduce((x,v,i)=>v?x+i.toString(36)+v:x))


Attempt This Online!

Input array of tokens, output array of values or throw error.

# JavaScript (ES6), 263 bytes

s=>s.replace(/\S+/g,v=>v>{}?s[0][1]=[[v,1]]:s=[[(u=[])|v||([[y,q],[x,u],...s]=s,v+1|0?e|=u+''!=q:q.map(([U,e])=>(u.find(o=>o[0]==U)||u.push(o=[U,0])&&o)[1]+=v<"/"?e:-e),eval(x+v+y)),u.filter(o=>o[1]).sort()],...s],e=s=[])|e?"*":s.reverse().join .split,.join


Try it online!

# Uiua, 204 192 178 153 characters

-12: remove error handling and output reversal and redundant spaces that the formatter always puts back in.
-14: use "uiuisms" page to golf character range, use content ◇ instead of unbox, and golf creation of the stack.
-25: Get less stingy with memory usage, etc.

P←⊙:⊙⊙°⊂∩°⊂°⊂◌
r←⇡27
a←⍜°⊂(@ ◌)+@r
i←⊙(⟨⊂⊂⊓×+P|⊂⊂⊓+⟜(⍤"*"≍)P|◌|◌|◌|⊂⊂⊓÷-P|⟨⍜⊙°⊂+=a|⊂×=0r⋕⟩⍣(1◌⋕)0.⟩↧6/+-@*.):°□°⊂
(↘1/◇⊂♭≡(⨬(□""◌◌|□⊂⊙°⋕)≠0,a)◌⍥i⧻.:↯0r)

P ← ⊙:⊙⊙°⊂∩°⊂°⊂ # pop 2 from small stack and separate number and units
r ← ⇡27
a ← ⍜°⊂(@ ◌)+@r
i ← ⊙(
⨬(
⊂⊂⊓×+P◌        # times
| ⊂⊂⊓+⟜(⍤"*"≍)P◌ # plus
| ◌
| ◌
| ◌       # other characters
| ⊂⊂⊓÷-P◌ # divided by
| ⨬(
⍜⊙°⊂+=a # letter: add one to corresponding cell in top of small stack
| ⊂×=0r⋕  # number: push unitless number to small stack
)⍣(1◌⋕)0.
)↧6/+-@*. # ---the difference from star, or 6 if it's a number or letter
):°□°⊂      # process one token from the input
f ← (
◌⍥i⧻.:↯0r             # setup the label and small stack
≡(⨬(□""◌◌|□⊂⊙°⋕)≠0,a) # append unit to each number in the small stack
↘1/◇⊂♭                # final concatenation
)


Input is a boxed array ({}) of token strings, or there is an extra string parsing function in the formatted version link.

# Charcoal, 123 116 bytes

Ｆ⪪Ｓ ¿Σι⊞υ⁺⟦Ｉι⟧Ｅβ⁰¿№βι§≔§υ±¹⊕⌕βι¹«≔⊟υζ≔⊟υη⊞υ∧∨⬤∧ζη∨¬λ⁼κ§ζλ⁻+ιＥ∧ζη⎇λ⁻κ×§ζλ⊖﹪℅ι³§⟦×κ§ζλ⁺κ§ζλ÷κ§ζλ⟧℅ι»Ｅυ⭆∨ι*⎇μ⎇λ⁺§β⊖μλωλ


Try it online! Link is to verbose version of code. Since the whole stack is output, I've effectively included several test cases in the link. Explanation:

Ｆ⪪Ｓ


Split the input on spaces and loop over the resulting tokens.

¿Σι⊞υ⁺⟦Ｉι⟧Ｅβ⁰


If this token is a positive integer, then push its value with zero exponents for all letters.

¿№βι§≔§υ±¹⊕⌕βι¹


Otherwise, if this token is a letter, then set the exponent for that letter to 1 for the latest value.

«


Otherwise:

≔⊟υζ≔⊟υη


Retrieve the two arguments.

⊞υ∧∨⬤∧ζη∨¬λ⁼κ§ζλ⁻+ιＥ∧ζη⎇λ⁻κ×§ζλ⊖﹪℅ι³§⟦×κ§ζλ⁺κ§ζλ÷κ§ζλ⟧℅ι


If the arguments are compatible or the operations is not sum, calculate the product, sum and quotient of the two arguments, selecting according to the ordinal of the token. (In the case of the exponents, the ordinal modulo 3 is decremented and multiplied by the second exponent, and this is subtracted from the first exponent.)

»Ｅυ⭆∨ι*⎇μ⎇λ⁺§β⊖μλωλ


For each value on the stack, concatenate its value with any non-zero exponents, unless the value was invalid, in which case output a *.