11
\$\begingroup\$

(FB, or Functional-Basic, as found here Interpret Functional-Basic)

Task

Implement a linting program for FB, similar to JSLint for JavaScript.

Error types

Invalid identifier

Identifiers can only have upper and lower case ASCII letters.

Locations:

  • The identifier right after a let expression
  • The identifier in a for loop

Examples:

for (? in b) <-- ? is invalid
let b_b8 = ... <-- b_b8 is invalid

Undefined identifier

If you encounter an identifier that has not been defined in either a let expression or a for loop. Note that the identifier in a for loop only count in the expression following it.

Location:

  • Anywhere an identifier is used

Example program:

let b = a <-- nope
for (x in a) x+1 <-- nope, still don't know what `a` is
print x <-- gotcha! `x` is out of scope here

Unexpected EOL

End of line is the deliminator for statements in FB, so you can't have a statement span multiple lines (even if it's surrounded by parenthesis ()). Use this if you expected more but encountered a EOL char.

Locations:

  • Any line

Examples:

let b <-- missing the `= [statement]`
for ( <-- missing `x in y) [statement]`
print <-- print what?
print print sum <-- sum what?

Unexpected token

This is best explained by examples.

Locations:

  • Any line

Examples:

print * 2 <-- What is `*2` doing for me? I need a statement!
3**5      <-- `3*` expects a number, not a `*` symbol
for x in y <-- a little diffrent, but for
                     expects a pren `(`, not x

Improper type

Used when a list is expected and a number was encountered, or when a number is needed and you get a list. Lists in FB are only allowed to have numbers or other lists as children, so errors can occur here as well. Note that if you find an identifier that is valid, you should perform this check based on the id's type.

Locations:

  • Function application
  • for loops
  • The +-*/^ operators
  • List literal creation

Examples:

let y = [5]
let z = y + [[], [6]] <-- invalid, y is a list of numbers, right
                            hand side is a list of lists
print y + 1           <-- y is a list, not a number
sum 1                 <-- sum takes a list, not a number
[5] - [5]             <-- invalid, subtraction doesn't apply to lists
1 + []                <-- addition doesn't work with a list and a number
for (digit in 12) ... <-- `for` takes a list
let list = [00, 00]
let nlst = [list, 1]  <-- invalid
[1, []]               <-- invalid

Multiple assignment

This is when a for loop or let statements identifiers overlap with a currently defined identifier.

Locations:

  • In a for loop
  • In a let statement

Examples:

let y = 8
let y = 5 <-- invalid, y is already defined
for (y in []) <-- invalid, y is already defined
for (x in []) ...
let x = 8 <-- valid since the `x` in the for loop is out of scope

Nested for loop

Again, pretty self explanatory. FB doesn't allow nested for loops.

Locations:

  • In for loops

Examples:

for (x in [[1, 5], [2, 8]]) for (y in x) x*y <-- just invalid

Input format

If you are writing a function, you will receive one multi-line string. If you are writing a full program, you must take a multi-line input.

Output format

Output format is simply the error title, followed by the line number the error occurs on. You must show all errors in the program.

Format:

[error title] on line [line number, 1-indexed]

Example

This is one large example that should show all possible error types and outputs. Please let me know if I missed something.

Input

let bb8 = 0
let bbEight = lst

for (x in 
let x = 5*/5
let y = 4
let z = [5, 6] + y
let y = [[4]] + [y]
for (x in [[y, y+1], 5]) for (y in x) 

Output

Invalid identifier on line 1
Undefined identifier on line 2
Unexpected EOL on line 4
Unexpected token on line 5
Improper type on line 6
Improper type on line 7
Multiple assignment on line 7
Nested for loop on line 8
Multiple assignment on line 8
Multiple assignment on line 8
Improper type on line 8
Unexpected EOL on line 8

Note that thees checks may come in any order, and if the same error occurs multiple times on the same line, you must print the error multiple times as well.

I hope this isn't to difficult

Edits

I have removed the Empty line error since it didn't make sense, and nobody is working on the problem (to my knowledge).

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Python3, 2381 bytes:

import re
N=lambda x:next(x,0)
G={'sum':[[2,1],1],'product':[[2,1],1],'print':[-1,[2]]}
P={'+':[(1,1),(2,2)]}
def s(l):
 r=[]
 while l:r+=[j:=re.search('\w+|\(|\)|\=|\+|\-|\*|/|\^|\[|\]|,|\s+|\W+',l).group()];l=l[len(j):]
 return filter(lambda x:x.strip(),r)
M=['Invalid identifier','Undefined identifier','Unexpected EOL','Unexpected token','Improper type','Multiple assignment','Nested for loop']
B=lambda x:x if int==type(x)else x[0]
F=lambda I,n:M[I]+f' on line {n}'
def O(l,n,E,t=-1,c=2):
 if l==0:c=2
 if t==-2:c=c
 if (t==-1 and l)or l==t:return l
 E(F(c,n));raise Exception
def A(b,s,k,v):
 if v:b[s][k]=v
def X(l,n,s,b,E,C,L=0,W=0):
 v,U=0,0
 while 1:
  if(k:=N(l))==0 and v:break
  if O(k,n,E)not in[')',']']:
   if k in'+-*/^':
    if v==0:O(1,n,E,-2,3)
    V,l=X(l,n,s,b,E,C)
    if(B(v),B(V))not in P.get(k,[(1,1)])or v!=V:O(1,n,E,-2,4)
    v=V;U=1
   elif U and k!=',':O(0,n,E,-2,3)
   elif k==',':l=iter([k,*l])
   if'for'==k:
    if L:E(F(6,n))
    v=C(iter([k,*l]),n,s,b,E);U=1
   elif'('==k:
    v,l=X(l,n,s,b,E,C);O(N(l),n,E,')',2);U=1
   elif k.isalpha():
    if k in G:
     V,l=X(l,n,s,b,E,C,0,1)
     if V==0:O(0,n,E)
     if G[k][0]!=-1 and G[k][0]!=V:O(1,n,E,-2,4)
     v=G[k][1];U=1
    else:
     if k not in b[s]:O(1,n,E,-2,1)
     v=b[s][k]
    U=1
   elif k.isdigit():
    v=1;U=1
   elif'['==k:
    L=[]
    while 1:
     V,l=X(l,n,s,b,E,C)
     if V:
      L+=[V]
      if(k:=N(l))==']':v=L;break
      O(k,n,E,',',3)
     else:O(O(N(l),n,E),n,E,']',3);break
    if(_:=len(Y:=set(map(str,L))))==1:v=[2,eval([*Y][0])]
    else:
     if _>1:E(F(4,n))
     v=[2]+([]if[]==L else[-1])
    U=1
   else:break
   if U and W:break
  else:l=iter([k,*l]);break
 return v,l
def p(l,n,s,b,E):
 k=N(l)
 if'let'==k:
  if O(k:=N(l),n,E).isalpha()==0:E(F(0,n))
  else:
   if(I:=(k in b[s])):E(F(5,n))
   O(x:=N(l),n,E);O(x,n,E,'=',3);V,l=X(l,n,s,b,E,p)
   if not I:A(b,s,k,V)
 elif'for'==k:
  O(N(l),n,E,'(',3);O(x:=N(l),n,E);O(1,n,E,x.isalpha(),0);O(m:=N(l),n,E);O(m,n,E,'in',3)
  if x in b[s]:E(F(5,n))
  V,l=X(l,n,s,b,E,p)
  O(V,n,E);O(N(l),n,E,')',2)
  if type(V)==int:E(F(4,n))
  V,l=X(l,n,(0,n),{**b,(0,n):{x:V if type(V)==int else V[1]}},E,p,1);O(V,n,E);return[2,V]
  return[2]
 elif k in G:l=iter([k,*l]);V,l=X(l,n,s,b,E,p)
def D(l):
 b,E={(0,):{}},[]
 for i,a in enumerate(filter(None,l.split('\n')),1):
  try:p(s(a),i,(0,),b,E.append)
  except:1
 return E

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ 2320 \$\endgroup\$
    – naffetS
    May 21, 2022 at 18:28
  • \$\begingroup\$ You sir are an archaeologist. \$\endgroup\$
    – J Atkin
    May 22, 2022 at 21:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.