Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Comments on Adding support on mathematical expressions

Post

Adding support on mathematical expressions

+0
−5

MarkFuncs has really gotten huge progress, at least in my eyes, and now, I want to make math possible in the language.

Background

MarkFuncs is a programming language that I have been working on for a while, which is basically a golflang that works like an object-oriented proglang (the summary for the final product). Currently, the language doesn't have a site to run the language, but it does have an interpreter that can run in TIO. Just copy this code into the Code section, then write a program on the Input section:

from ast import arg
from inspect import signature

BAD_RESULT = "err"

def bottles_of_beer(num):
	for i in range(num, 0, -1):
		if i == 1:
			if num == 1:
				print(f"1 bottle of beer on the wall. 1 bottle of beer.\nLet it be to sit in peace. {num} bottle of beer on the wall.")
			else:
				print(f"1 bottle of beer on the wall. 1 bottle of beer.\nGo to the store and buy some more. {num} bottles of beer on the wall.")
		elif i == 2:
			print(f"{i} bottles of beer on the wall. {i} bottles of beer.\nTake one down and pass it around. {i - 1} bottle of beer on the wall.\n")
		else:
			print(f"{i} bottles of beer on the wall. {i} bottles of beer.\nTake one down and pass it around. {i - 1} bottles of beer on the wall.\n")

def str_replicator(str, num):
	for chr in str:
		print(end = chr * num)

functions = {
	"bottle": bottles_of_beer,
	"repl": str_replicator
}

variables = {}

"""
Helper method
returns (string, new index)
"""
def getString(code, i):
	assert(code[i] == '"')
	i += 1
	str = ""
	while code[i] != '"':
		str += code[i]
		i += 1
	i += 1
	return str, i

def getNum(code, i):
	assert(code[i].isdigit() or code[i] == '.')
	float_check = 0
	num = ""
	while code[i].isdigit() or code[i] == '.':
		if code[i] == '.':
			float_check += 1
		num += code[i]; i += 1
		if float_check > 1:
			print("Invalid syntax → Reuse of decimal points", file = sys.stderr)
			return BAD_RESULT, i
	if float_check == 0:
		return int(num), i
	else:
		return float(num), i

def program(code):
	i = 0
	while i < len(code):
		if code[i] == '→':
			if code[i+1] == '"':
				i += 2
				str = ""
				while code[i] != '"':
					str += code[i]; i += 1
				print(str)
				i += 1
			elif code[i+1].isdigit():
				i += 1
				equation = ""
				while code[i] != ';':
					equation += code[i]; i += 1
				i += 1
			elif code[i+1].isalpha():
				i += 1
				var = ""
				while code[i] != ';':
					var += code[i]; i += 1
				print(variables[var])
				i += 1
			else:
				print(f"Invalid syntax → {code[i]} is not a variable", file = sys.stderr)
		elif code[i].isalpha():
			funcname = ""
			while code[i].isalpha() or code[i].isdigit():
				funcname += code[i]; i += 1
			if code[i] == '(':
				args = []
				i += 1
				while (code[i] != ')'):
					if (code[i] == '"'):
						arg, i = getString(code, i)
					elif (code[i].isdigit() or code[i] == '.'):
						arg, i = getNum(code, i)
					else:
						print(f"Invalid argument → {code[i]} can't be an argument", file = sys.stderr)
					args.append(arg)
					if (code[i] == ','):
						i += 1
					else:
						assert(code[i] == ')')
				i += 1
				if functions[funcname]:
					func = functions[funcname]
					num_req_args = len(signature(func).parameters)
					if (num_req_args != len(args)):
						print(f"Not enough arguments → {funcname} requires {num_req_args} but only {len(args)} were given", file = sys.stderr)
					else:
						func(*args)
				else:
					print(f"Invalid function → {funcname}() does not exist", file = sys.stderr)
			elif code[i] == '=':
				i += 1
				if (code[i] == '"'):
					val, i = getString(code, i)
				elif (code[i].isdigit() or code[i] == '.'):
					val, i = getNum(code, i)
				i += 1
				variables[funcname] = val
		else:
			print(f"Invalid syntax → Character {code[i]} undefined", file = sys.stderr)

while True:
	try:
		s = input()
		program(s)
	except:
		break

An example program you can run is:

var1="Hello, ";
→var1;var2="World!";
→var2;

The language is obviously incomplete, and I'm using Q&A so I can add more stuff, not just to rely on the Discord server.

Main Deal

I currently want to implement the mathematical expressions in programming. There are a few things to deal with:

  1. Calculate with integers and floating-point numbers:
→3+5; ` Full program
→     ` Output function
 3+5  ` Add 3 and 5
    ; ` Trigger to start a new line

Backticks are supposed to be the syntax for comments, which will be implemented soon.

  1. Calculate variables:
x=10;y=15;→x+=y; ` Full program
x=10;            ` Assign x with 10
     y=15;       ` Assign y with 15
          →x+=y; ` Output x+y, assign x with sum of x and y
  1. Access the values of variables in functions
i=100;bottle(i-1); ` Full program
i=100;             ` Assign i with 100
      bottle(i-1); ` Run "N Bottles of Beer" using i+1 (see tutorial for more info)

I currently have no idea how to implement this properly, especially with the syntax I have involved:

+ plus
- minus
* times
/ divided by
% modulo
^ exponentiate
⊕ XOR

Use GOEMDAS when doing math:
- Grouped equations first
- Functions such as modulo and XOR go first
- Exponents are next
- MD: if multiplication goes first in equation, do all multiplication, same goes for division
- AS: if addition goes first in equation, do all addition, same goes for subtraction

So how can I properly implement this system under MarkFuncs syntax?

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

3 comment threads

Side note: perhaps you're not doing it right (or at least, not the best way) (2 comments)
Precedence (2 comments)
Expression trees or perhaps it's better for you to stick with RPN (5 comments)
Expression trees or perhaps it's better for you to stick with RPN
elgonzo‭ wrote over 2 years ago · edited over 2 years ago

Given an expression (no matter how complex) in your language, let your parser internally build an expression tree, taking operator precedence into account. Such a tree would be a binary tree, where each node can only have zero, one or two leaves. A leaf-less node would represent either a value, or a function call that returns a value. Any node with one or two leaves would represent an unary or binary operator, with an unary operator being a node with a single leaf, and an binary operator being a node with two leaves for the left and right argument of that operation. I don't know whether different datatypes should be supported (int vs. float, for example) and how different datatypes might effect the actual result of an operation (think of integer division, for example), but if there are different datatypes to be supported you would also need to consider that before starting to hack up some code.

elgonzo‭ wrote over 2 years ago · edited over 2 years ago

The expression tree is not necessarily required to be an explict data structure to be built. In can be implicitly (or conceptually, if you will) realized by a clever implementation of an interpreting parser that evaluates the expression as it interprets it, but this can be trickier to get right than just building an explicit expression tree (think of a + b * c - note that b is not the right-hand argument for the + operator, but b * c is, making it necessary for an interpreting parser to maintain some state with regard to the expression parsing to be eventually able to resolve the sub expressions in the correct fashion; that state information would be more or less equivalent to the information conveyed by the tree structure itself in an expression tree.) Doing the expression-tree-based approach for the first time might be quite the effort, depending on your experience level and/or how confident you are in your programming skills with regard to building/altering tree structures.

elgonzo‭ wrote over 2 years ago · edited over 2 years ago

On the other hand, you might perhaps spend some thoughts on using RPN (Reverse Polish Notation) instead. While it might not be like the common infix-notation humans are normally used to [*] (on the other hand, early HP pocket calculators and early ones of some other brands used RPN, so there is that...), it would be relatively simple to implement and could be entirely stack-based. Parsing and evaluating RPN-based expressions would be far simpler than parsing the more conventional syntax and building expression trees. ( [*] Then again, your goal is to write a language for code golfing, so "what humans are normally used to" should not be that important anyways... ;-)

General Sebast1an‭ wrote over 2 years ago

elgonzo‭ That's a ton of text I have to decrypt then. Thanks for the advice, I'll see what I can do.

elgonzo‭ wrote over 2 years ago · edited over 2 years ago

Just as a side note, because i put the stack-based RPN approach against expression trees. On a conceptual/theoretical level, they are not so far removed from each other. The stack-based RPN approach is in fact the same as post-order traversal of an expression tree -- except the tree doesn't have to be built and (separately) evaluated by traversing it, because the RPN rules require the operands and operators to be placed in a sequence that coincides with the sequence of operands and operators when post-order traversing the equivalent expression tree. This saves the RPN parser from having to actually build an expression tree -- that task is basically shifted indirectly to the user writing/inputting an RPN expression :-)