• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 265
  • Last Modified:

Evaluating strings

Like If I have B as string and A as integer
B="(3+5)"

how do I get A = 5 by evaluting the contents of string B.
In foxpro you would do A = &B
0
fuee
Asked:
fuee
  • 8
  • 4
  • 3
  • +1
1 Solution
 
GivenRandyCommented:
A = val(B)
0
 
GivenRandyCommented:
Not what you asked, but I know the answer.  Use the Eval function.  Sorry for jumping the gun on the first response.
0
 
GivenRandyCommented:
Add the Microsoft Script Control to your project (from Project -> Components).  Then use code like the following:

Dim Question As String
Dim Answer As String
   
Question = "4+5"
Answer = ScriptControl1.Eval(Question)

Of course, you can change answer to be whatever you want (output, messagebox, etc.).
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
AzraSoundCommented:
see this previously asked question:

http://www1.experts-exchange.com/bin/Q.10333271
0
 
GivenRandyCommented:
AzraSound, the functions on that page are extremely basic.  They might be good for very simple functions, but hardly better than anything that Visual Basic already provides.  Sorry.

Besides, it will cost 5 question points....

Azrasound, I'm not making this personal, so please don't take it that way.  The answer you gave hardly answered the question (I don't think it did, someone else's answer did), yet you got an A.  Yesterday I answered someone's question with a "perfect" answer and got a D!  The answer was a near-perfect duplication of someone who got an A.  Go figure!
0
 
AzraSoundCommented:
i dont think you looked closely enough at the link that i posted that was accepted as an answer. at that link is another link that says "parse expressions". clicking on it takes you to a link to download the sample project. it is one of the more powerful expression parsers out there wrapping up the functionality into a well designed class module.  it handles all basic expressions including multiplication, division, addition, subtraction, parenthesis, and exponents.  i highly recommend you download it yourself and take a look at it.  here is the direct link:

http://www.vbweb.co.uk/commands/math_functions.htm#parseexpression

0
 
AzraSoundCommented:
regarding your statement about receiving bad grades, yes it is extremely frustrating.  i think there have been suggestions made to allow for a way for someone to refuse a grade they receive.  often times we'd rather get no grade at all than a 'C' or a 'D'.  it's really annoying b/c the whole reason the person came to this site was to get information about a problem they obviously couldnt solve on their own...and then when presented with a working solution they actually have the nerve to give a low mark?  absolutely ridiculous...
0
 
GivenRandyCommented:
Amen.  Thanks for the URL!
0
 
deightonCommented:
Use the following e_eval function


Option Explicit
Dim e_input As String     ' Expression input string.
Dim e_tok As String       ' Current token kind.
Dim e_spelling As String  ' Current token spelling.
Dim e_error As Integer    ' Tells if syntax error occurred.

Function e_eval(ByVal s As String, value As Double) As Integer
   ' Initialize.
   e_error = 0
   e_input = s
   Call e_nxt
 
   ' Evaluate.
   value = e_prs(1)
 
   ' Check for unrecognized input.
   If e_tok <> "" And Not e_error Then
      MsgBox "syntax error, token = '" + e_spelling + "'"
   e_error = -1
   End If
 
   e_eval = Not e_error
End Function
 
' e_prs
'   Parse an expression, allowing operators of a specified
'   precedence or higher. The lowest precedence is 1.
'   This function gets tokens with e_nxt and recursively
'   applies operator precedence rules.
Function e_prs(p As Integer) As Double
   Dim n As Double    ' Return value.
   Dim fun As String  ' Function name.
 
   ' Parse expression that begins with a token (precedence 12).
   If e_tok = "num" Then
      ' number.
      n = Val(e_spelling)
      Call e_nxt
   ElseIf e_tok = "-" Then
      ' unary minus.
      Call e_nxt
      n = -e_prs(11)    ' Operand precedence 11.
   ElseIf e_tok = "not" Then
      ' logical NOT.
      Call e_nxt
      n = Not e_prs(6)  ' Operand precedence 6.
   ElseIf e_tok = "(" Then
      ' parentheses.
      Call e_nxt
      n = e_prs(1)
      Call e_match(")")
   ElseIf e_tok = "id" Then
      ' Function call.
      fun = e_spelling
      Call e_nxt
      Call e_match("(")
      n = e_prs(1)
      Call e_match(")")
      n = e_function(fun, n)
   Else
      If Not e_error Then
         MsgBox "syntax error, token = '" + e_spelling + "'"
         e_error = -1
      End If
   End If
 
   ' Parse binary operators.
Do While Not e_error
  If 0 Then  ' To allow ElseIf .
  ElseIf p <= 11 And e_tok = "^" Then: Call e_nxt: n = n ^ e_prs(12)
  ElseIf p <= 10 And e_tok = "*" Then: Call e_nxt: n = n * e_prs(11)
  ElseIf p <= 10 And e_tok = "/" Then: Call e_nxt: n = n / e_prs(11)
  ElseIf p <= 9 And e_tok = "\" Then: Call e_nxt: n = n \ e_prs(10)
  ElseIf p <= 8 And e_tok = "mod" Then: Call e_nxt: n = n Mod e_prs(9)
  ElseIf p <= 7 And e_tok = "+" Then: Call e_nxt: n = n + e_prs(8)
  ElseIf p <= 7 And e_tok = "-" Then: Call e_nxt: n = n - e_prs(8)
  ElseIf p <= 6 And e_tok = "=" Then: Call e_nxt: n = n = e_prs(7)
  ElseIf p <= 6 And e_tok = "<" Then: Call e_nxt: n = n < e_prs(7)
  ElseIf p <= 6 And e_tok = ">" Then: Call e_nxt: n = n > e_prs(7)
  ElseIf p <= 6 And e_tok = "<>" Then: Call e_nxt: n = n <> e_prs(7)
  ElseIf p <= 6 And e_tok = "<=" Then: Call e_nxt: n = n <= e_prs(7)
  ElseIf p <= 6 And e_tok = ">=" Then: Call e_nxt: n = n >= e_prs(7)
  ElseIf p <= 5 And e_tok = "and" Then: Call e_nxt: n = n And e_prs(6)
  ElseIf p <= 4 And e_tok = "or" Then: Call e_nxt: n = n Or e_prs(5)
  ElseIf p <= 3 And e_tok = "xor" Then: Call e_nxt: n = n Xor e_prs(4)
  ElseIf p <= 2 And e_tok = "eqv" Then: Call e_nxt: n = n Eqv e_prs(3)
  ElseIf p <= 1 And e_tok = "imp" Then: Call e_nxt: n = n Imp e_prs(2)
  Else
       Exit Do
  End If
 Loop
 
   e_prs = n
End Function
 
' e_function.
'   Evaluate a function. This is a helper function to simplify
'   e_prs.
Function e_function(fun As String, arg As Double) As Double
   Dim n As Double
 
   Select Case LCase$(fun)
   Case "abs": n = Abs(arg)
   Case "atn": n = Atn(arg)
   Case "cos": n = Cos(arg)
   Case "exp": n = Exp(arg)
   Case "fix": n = Fix(arg)
   Case "int": n = Int(arg)
   Case "log": n = Log(arg)
   Case "rnd": n = Rnd(arg)
   Case "sgn": n = Sgn(arg)
   Case "sin": n = Sin(arg)
   Case "sqr": n = Sqr(arg)
   Case "tan": n = Tan(arg)
   
   
   'New functions
   Case "arccos": n = arccos(arg)
   Case "arcsin": n = arcsin(arg)
   
   Case "arcsec": n = arcsec(arg)
   Case "arccosec": n = arccosec(arg)
   Case "arccot": n = arccot(arg)
   
   Case "cot": n = cot(arg)
   Case "cosec": n = cosec(arg)
   Case "sec": n = sec(arg)
   
   Case "sinh": n = sinh(arg)
   Case "cosh": n = cosh(arg)
   Case "tanh": n = tanh(arg)
   
   Case "arcsinh": n = arcsinh(arg)

   Case "coth": n = coth(arg)
   Case "cosech": n = cosech(arg)
   Case "sech": n = sech(arg)


   Case Else
      If Not e_error Then
         MsgBox "undefined function '" + fun + "'"
         e_error = -1
      End If
   End Select
 
   e_function = n
End Function


Private Function arccos(ByVal x As Double) As Double
   
    If Abs(x) = 1 Then
   
        If x = 1 Then arccos = 0 Else arccos = Atn(1) * 4
   
    Else
   
        arccos = Atn(-x / Sqr(-x * x + 1)) + 2 * Atn(1)

    End If
   
End Function
   
Private Function arcsec(ByVal x As Double) As Double
   
    arcsec = arccos(1 / x)
   
End Function

Private Function arcsinh(ByVal x As Double) As Double
   
    arcsinh = Log(x + Sqr(x * x + 1))
       
End Function

Private Function arccosec(ByVal x As Double) As Double
   
    arccosec = arcsin(1 / x)
   
End Function

Private Function arccot(ByVal x As Double) As Double
   
    arccot = Atn(1 / x)

   
End Function

Private Function sec(ByVal x As Double) As Double
   
    sec = 1 / Cos(x)
       
End Function

Private Function cot(ByVal x As Double) As Double
   
    cot = 1 / Tan(x)
       
End Function

Private Function cosec(ByVal x As Double) As Double
   
     cosec = 1 / Sin(x)
       
End Function


Private Function sinh(ByVal x As Double) As Double
   
    sinh = (Exp(x) - Exp(-x)) / 2
           
End Function

Private Function cosh(ByVal x As Double) As Double
   
    cosh = (Exp(x) + Exp(-x)) / 2
           
End Function

Private Function tanh(ByVal x As Double) As Double
   
    tanh = sinh(x) / cosh(x)
           
End Function

Private Function coth(ByVal x As Double) As Double
   
    coth = 1 / tanh(x)
           
End Function

Private Function sech(ByVal x As Double) As Double
   
    sech = 1 / cosh(x)
           
End Function

Private Function cosech(ByVal x As Double) As Double
   
    cosech = 1 / sinh(x)
           
End Function

Private Function arcsin(ByVal x As Double) As Double
   
    If Abs(x) = 1 Then
   
        arcsin = Atn(1) * 2 * Sgn(x)
   
    Else
   
        arcsin = Atn(x / Sqr(-x * x + 1))

    End If
   
End Function
   
   
 
' e_nxt
'   Get the next token into e_tok and e_spelling and remove the
'   token from e_input.
'   This function groups the input into "words" like numbers,
'   operators and function names.
Sub e_nxt()
   Dim is_keyword As Integer
   Dim c As String  ' Current input character.
   Dim is_id As Integer
 
   e_tok = ""
   e_spelling = ""
 
   ' Skip whitespace.
   Do
      c = Left$(e_input, 1)
      e_input = Mid$(e_input, 2)
   Loop While c = " " Or c = Chr$(9) Or c = Chr$(13) Or c = Chr$(10)
 
   Select Case LCase$(c)
 
   ' Number constant. Modify this to support hexadecimal, etc.
   Case "0" To "9", "."
      e_tok = "num"
      Do
         e_spelling = e_spelling + c
         c = Left$(e_input, 1)
         e_input = Mid$(e_input, 2)
      Loop While (c >= "0" And c <= "9") Or c = "."
      e_input = c + e_input
 
   ' Identifier or keyword.
   Case "a" To "z", "_"
      e_tok = "id"
      Do
         e_spelling = e_spelling + c
         c = LCase$(Left$(e_input, 1))
         e_input = Mid$(e_input, 2)
         is_id = (c >= "a" And c <= "z")
         is_id = is_id Or c = "_" Or (c >= "0" And c <= "9")
      Loop While is_id
      e_input = c + e_input
 
      ' Check for keyword.
      is_keyword = -1
      Select Case LCase$(e_spelling)
         Case "and"
         Case "eqv"
         Case "imp"
         Case "mod"
         Case "not"
         Case "or"
         Case "xor"
         Case Else: is_keyword = 0
      End Select
      If is_keyword Then
         e_tok = LCase$(e_spelling)
      End If
 
   ' Check for <=, >=, <>.
   Case "<", ">"
      e_tok = c
      c = Left$(e_input, 1)
      If c = "=" Or c = ">" Then
         e_tok = e_tok + c
         e_input = Mid$(e_input, 2)
      End If
 
   ' Single character token.
   Case Else
      e_tok = c
   End Select
 
    If e_spelling = "" Then
       e_spelling = e_tok
   End If
End Sub
 
' e_match
'   Check the current token and skip past it.
'   This function helps with syntax checking.
Sub e_match(token As String)
   If Not e_error And e_tok <> token Then
      MsgBox "expected " + token + ", got '" + e_spelling + "'"
      e_error = -1
   End If
   Call e_nxt
End Sub
Private Sub Command1_Click()
   
       Dim n As Double
 
       If e_eval(Text1.Text, n) Then
         Label3.Caption = Format$(n)
       End If
   
End Sub


0
 
fueeAuthor Commented:
I am asking for a VB Function.....not a parsing routine for the string. I mean if there isn't any. Just say "There isnt any"
0
 
deightonCommented:
Did you try my code sample?  

Maybe its use not clear enough!

Private Sub Command1_Click()
   
       Dim n As Double
 
       'e.g. text1.text = "2 + 2 - cos(1)"
       If e_eval(Text1.Text, n) Then
         Label3.Caption = Format$(n)
       End If
   
End Sub

There is not a built in VB function in my version of VB, unless you download an add in that gives you the function.  This is a user written function.

Good luck!



0
 
deightonCommented:
Here's what you do.

Add a .bas module to the project and copy the following into the .bas module.

Now in your project you have a vb function

eval("2+2")

e.g

debug.print eval("2+2-5")



Option Explicit
Dim e_input As String     ' Expression input string.
Dim e_tok As String       ' Current token kind.
Dim e_spelling As String  ' Current token spelling.
Dim e_error As Integer    ' Tells if syntax error occurred.

Private Function e_eval(ByVal s As String, value As Double) As Integer
   ' Initialize.
   e_error = 0
   e_input = s
   Call e_nxt
 
   ' Evaluate.
   value = e_prs(1)
 
   ' Check for unrecognized input.
   If e_tok <> "" And Not e_error Then
      MsgBox "syntax error, token = '" + e_spelling + "'"
   e_error = -1
   End If
 
   e_eval = Not e_error
End Function
 
' e_prs
'   Parse an expression, allowing operators of a specified
'   precedence or higher. The lowest precedence is 1.
'   This function gets tokens with e_nxt and recursively
'   applies operator precedence rules.
Private Function e_prs(p As Integer) As Double
   Dim n As Double    ' Return value.
   Dim fun As String  ' Function name.
 
   ' Parse expression that begins with a token (precedence 12).
   If e_tok = "num" Then
      ' number.
      n = Val(e_spelling)
      Call e_nxt
   ElseIf e_tok = "-" Then
      ' unary minus.
      Call e_nxt
      n = -e_prs(11)    ' Operand precedence 11.
   ElseIf e_tok = "not" Then
      ' logical NOT.
      Call e_nxt
      n = Not e_prs(6)  ' Operand precedence 6.
   ElseIf e_tok = "(" Then
      ' parentheses.
      Call e_nxt
      n = e_prs(1)
      Call e_match(")")
   ElseIf e_tok = "id" Then
      ' Function call.
      fun = e_spelling
      Call e_nxt
      Call e_match("(")
      n = e_prs(1)
      Call e_match(")")
      n = e_function(fun, n)
   Else
      If Not e_error Then
         MsgBox "syntax error, token = '" + e_spelling + "'"
         e_error = -1
      End If
   End If
 
   ' Parse binary operators.
Do While Not e_error
  If 0 Then  ' To allow ElseIf .
  ElseIf p <= 11 And e_tok = "^" Then: Call e_nxt: n = n ^ e_prs(12)
  ElseIf p <= 10 And e_tok = "*" Then: Call e_nxt: n = n * e_prs(11)
  ElseIf p <= 10 And e_tok = "/" Then: Call e_nxt: n = n / e_prs(11)
  ElseIf p <= 9 And e_tok = "\" Then: Call e_nxt: n = n \ e_prs(10)
  ElseIf p <= 8 And e_tok = "mod" Then: Call e_nxt: n = n Mod e_prs(9)
  ElseIf p <= 7 And e_tok = "+" Then: Call e_nxt: n = n + e_prs(8)
  ElseIf p <= 7 And e_tok = "-" Then: Call e_nxt: n = n - e_prs(8)
  ElseIf p <= 6 And e_tok = "=" Then: Call e_nxt: n = n = e_prs(7)
  ElseIf p <= 6 And e_tok = "<" Then: Call e_nxt: n = n < e_prs(7)
  ElseIf p <= 6 And e_tok = ">" Then: Call e_nxt: n = n > e_prs(7)
  ElseIf p <= 6 And e_tok = "<>" Then: Call e_nxt: n = n <> e_prs(7)
  ElseIf p <= 6 And e_tok = "<=" Then: Call e_nxt: n = n <= e_prs(7)
  ElseIf p <= 6 And e_tok = ">=" Then: Call e_nxt: n = n >= e_prs(7)
  ElseIf p <= 5 And e_tok = "and" Then: Call e_nxt: n = n And e_prs(6)
  ElseIf p <= 4 And e_tok = "or" Then: Call e_nxt: n = n Or e_prs(5)
  ElseIf p <= 3 And e_tok = "xor" Then: Call e_nxt: n = n Xor e_prs(4)
  ElseIf p <= 2 And e_tok = "eqv" Then: Call e_nxt: n = n Eqv e_prs(3)
  ElseIf p <= 1 And e_tok = "imp" Then: Call e_nxt: n = n Imp e_prs(2)
  Else
       Exit Do
  End If
 Loop
 
   e_prs = n
End Function
 
' e_function.
'   Evaluate a function. This is a helper function to simplify
'   e_prs.
Private Function e_function(fun As String, arg As Double) As Double
   Dim n As Double
 
   Select Case LCase$(fun)
   Case "abs": n = Abs(arg)
   Case "atn": n = Atn(arg)
   Case "cos": n = Cos(arg)
   Case "exp": n = Exp(arg)
   Case "fix": n = Fix(arg)
   Case "int": n = Int(arg)
   Case "log": n = Log(arg)
   Case "rnd": n = Rnd(arg)
   Case "sgn": n = Sgn(arg)
   Case "sin": n = Sin(arg)
   Case "sqr": n = Sqr(arg)
   Case "tan": n = Tan(arg)
   
   
   'New functions
   Case "arccos": n = arccos(arg)
   Case "arcsin": n = arcsin(arg)
   
   Case "arcsec": n = arcsec(arg)
   Case "arccosec": n = arccosec(arg)
   Case "arccot": n = arccot(arg)
   
   Case "cot": n = cot(arg)
   Case "cosec": n = cosec(arg)
   Case "sec": n = sec(arg)
   
   Case "sinh": n = sinh(arg)
   Case "cosh": n = cosh(arg)
   Case "tanh": n = tanh(arg)
   
   Case "arcsinh": n = arcsinh(arg)

   Case "coth": n = coth(arg)
   Case "cosech": n = cosech(arg)
   Case "sech": n = sech(arg)


   Case Else
      If Not e_error Then
         MsgBox "undefined function '" + fun + "'"
         e_error = -1
      End If
   End Select
 
   e_function = n
End Function


Private Function arccos(ByVal x As Double) As Double
   
    If Abs(x) = 1 Then
   
        If x = 1 Then arccos = 0 Else arccos = Atn(1) * 4
   
    Else
   
        arccos = Atn(-x / Sqr(-x * x + 1)) + 2 * Atn(1)

    End If
   
End Function
   
Private Function arcsec(ByVal x As Double) As Double
   
    arcsec = arccos(1 / x)
   
End Function

Private Function arcsinh(ByVal x As Double) As Double
   
    arcsinh = Log(x + Sqr(x * x + 1))
       
End Function

Private Function arccosec(ByVal x As Double) As Double
   
    arccosec = arcsin(1 / x)
   
End Function

Private Function arccot(ByVal x As Double) As Double
   
    arccot = Atn(1 / x)

   
End Function

Public Function eval(ByVal target) As Double

    Dim n As Double
   
    Call e_eval(target, n)
    eval = n
   

End Function

Private Function sec(ByVal x As Double) As Double
   
    sec = 1 / Cos(x)
       
End Function

Private Function cot(ByVal x As Double) As Double
   
    cot = 1 / Tan(x)
       
End Function

Private Function cosec(ByVal x As Double) As Double
   
     cosec = 1 / Sin(x)
       
End Function


Private Function sinh(ByVal x As Double) As Double
   
    sinh = (Exp(x) - Exp(-x)) / 2
           
End Function

Private Function cosh(ByVal x As Double) As Double
   
    cosh = (Exp(x) + Exp(-x)) / 2
           
End Function

Private Function tanh(ByVal x As Double) As Double
   
    tanh = sinh(x) / cosh(x)
           
End Function

Private Function coth(ByVal x As Double) As Double
   
    coth = 1 / tanh(x)
           
End Function

Private Function sech(ByVal x As Double) As Double
   
    sech = 1 / cosh(x)
           
End Function

Private Function cosech(ByVal x As Double) As Double
   
    cosech = 1 / sinh(x)
           
End Function

Private Function arcsin(ByVal x As Double) As Double
   
    If Abs(x) = 1 Then
   
        arcsin = Atn(1) * 2 * Sgn(x)
   
    Else
   
        arcsin = Atn(x / Sqr(-x * x + 1))

    End If
   
End Function
   
   
 
' e_nxt
'   Get the next token into e_tok and e_spelling and remove the
'   token from e_input.
'   This function groups the input into "words" like numbers,
'   operators and function names.
Private Sub e_nxt()
   Dim is_keyword As Integer
   Dim c As String  ' Current input character.
   Dim is_id As Integer
 
   e_tok = ""
   e_spelling = ""
 
   ' Skip whitespace.
   Do
      c = Left$(e_input, 1)
      e_input = Mid$(e_input, 2)
   Loop While c = " " Or c = Chr$(9) Or c = Chr$(13) Or c = Chr$(10)
 
   Select Case LCase$(c)
 
   ' Number constant. Modify this to support hexadecimal, etc.
   Case "0" To "9", "."
      e_tok = "num"
      Do
         e_spelling = e_spelling + c
         c = Left$(e_input, 1)
         e_input = Mid$(e_input, 2)
      Loop While (c >= "0" And c <= "9") Or c = "."
      e_input = c + e_input
 
   ' Identifier or keyword.
   Case "a" To "z", "_"
      e_tok = "id"
      Do
         e_spelling = e_spelling + c
         c = LCase$(Left$(e_input, 1))
         e_input = Mid$(e_input, 2)
         is_id = (c >= "a" And c <= "z")
         is_id = is_id Or c = "_" Or (c >= "0" And c <= "9")
      Loop While is_id
      e_input = c + e_input
 
      ' Check for keyword.
      is_keyword = -1
      Select Case LCase$(e_spelling)
         Case "and"
         Case "eqv"
         Case "imp"
         Case "mod"
         Case "not"
         Case "or"
         Case "xor"
         Case Else: is_keyword = 0
      End Select
      If is_keyword Then
         e_tok = LCase$(e_spelling)
      End If
 
   ' Check for <=, >=, <>.
   Case "<", ">"
      e_tok = c
      c = Left$(e_input, 1)
      If c = "=" Or c = ">" Then
         e_tok = e_tok + c
         e_input = Mid$(e_input, 2)
      End If
 
   ' Single character token.
   Case Else
      e_tok = c
   End Select
 
    If e_spelling = "" Then
       e_spelling = e_tok
   End If
End Sub
 
' e_match
'   Check the current token and skip past it.
'   This function helps with syntax checking.
Private Sub e_match(token As String)
   If Not e_error And e_tok <> token Then
      MsgBox "expected " + token + ", got '" + e_spelling + "'"
      e_error = -1
   End If
   Call e_nxt
End Sub




0
 
GivenRandyCommented:
What was wrong with my answer?

It works and it does many of the functions others mentioned as well.

0
 
GivenRandyCommented:
For example, cos(), log().
0
 
deightonCommented:
I don't know what was wrong with your answer.  Since I fel my answer answers the question and the follow up comments I posted it.  Maybe he can't add the control for version reasons?
0
 
GivenRandyCommented:
Very possible.  Okay, I just wanted to make sure I wasn't missing the boat completely.  Thanks, deighton.  Party on, dudes.
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 8
  • 4
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now