Discuss Scratch
- Discussion Forums
- » Things I'm Making and Creating
- » (first post in TIMAC) I made a programming language in Python (the non-conventional way)
- scratch978654
- Scratcher
100+ posts
(first post in TIMAC) I made a programming language in Python (the non-conventional way)
So this is my first post here, so please be nice
I made a new programming language in Python. It isn't a full-fledged one yet, because there are no lambdas, try-catch statements, or even an elif. But I'm really proud of what I managed to do with only a lexer and the exec function to run Python scripts from strings. It has curly brace syntax.
Basically, most people make a programming language like this.
1. Lexer (tokenize based on operations, keywords, identifiers)
2. Parser (generate a parse tree for order of execution)
3. AST (Turn the parse tree into something more meaningful to the computer)
4. Run (Interpret AST)
Of course for compiled languages there's the additional step of generating machine code.
But I made it like this.
1. Lexer
2. Convert (turn list of tokens into Python script)
3. Run (using exec and catch all errors that appear)
So far, it supports:
=> Variables
You must use var even when changing the variable value.
=> Conditionals, else conditionals, type-dependent conditionals
You can loop like the repeat keyword.
A regular while loop works like an if statement, except “cond” is replaced with “while”.
Dowhile is like while but it runs the content at least once.
=> Classes, subclasses
=> Switch, case
=> Coerce
coerce('number', ‘5.8’) => 5.8
coerce('boolean', ‘') => false
coerce(’string', true) => ‘true’
=> foreach
Takes three parameters. Loops through an array
“index” is the current iteration.
“array” is the array itself.
=> Inline cond-else
Here's the Python code.
Give your feedback by testing it out yourself!
I made a new programming language in Python. It isn't a full-fledged one yet, because there are no lambdas, try-catch statements, or even an elif. But I'm really proud of what I managed to do with only a lexer and the exec function to run Python scripts from strings. It has curly brace syntax.
Basically, most people make a programming language like this.
1. Lexer (tokenize based on operations, keywords, identifiers)
2. Parser (generate a parse tree for order of execution)
3. AST (Turn the parse tree into something more meaningful to the computer)
4. Run (Interpret AST)
Of course for compiled languages there's the additional step of generating machine code.
But I made it like this.
1. Lexer
2. Convert (turn list of tokens into Python script)
3. Run (using exec and catch all errors that appear)
So far, it supports:
=> Variables
You must use var even when changing the variable value.
var x = 1=> “arrays” (basically Python lists)
array x // x becomes empty arrayAll Python array functions work with arrays.
=> Conditionals, else conditionals, type-dependent conditionals
cond x==1 {=> Loops with the “loop” keyword
write(“Hello”) // built-in function for print
}
else {
write(“Bye”)
}
You can loop like the repeat keyword.
loop 10 {You can also define custom tracker names with the “iden” keyword.
write(“loop”) // this will happen 10 times
// You can use “break” and “continue” whenever you want.
}
loop 10 iden n {=> While loops
write(n) // this loop is similar in nature to for n in range(10).
A regular while loop works like an if statement, except “cond” is replaced with “while”.
while x==1 {=> Dowhile, dowhile x
write(x)
var x = x+1
Dowhile is like while but it runs the content at least once.
whlie x<=10 do {Dowhile x is a variant and repeats the content at least x times.
write(x)
var x = x+1
}
while x<=10 do 5 {=> Functions
write(x)
var x = x+1
}
function square(num) {I did not have to code up return.
write(num*num)
}
=> Classes, subclasses
class Computer {To declare a new instance use the declare keyword
function class constructor(brand,os,inches) { // defines a constructor automatically called
this brand = brand
this os = os
this inches = inches
}
}
declare acerSwiftThree Computer('acer','windows',14)To extend a class. use extends.
class windowsComputer extends Computer {No need for a super constructor.
=> Switch, case
switch x {Default is replaced with case default. No need for break or brackets either.
case 1 {
write(“x is 1”)
}
case 2 {
write(“x is 2”)
}
case default {
write(“x is not 1 or 2”)
}
}
=> Coerce
coerce('number', ‘5.8’) => 5.8
coerce('boolean', ‘') => false
coerce(’string', true) => ‘true’
=> foreach
Takes three parameters. Loops through an array
foreach current index array in myArray {“current” is the element
write(current,index,array)
}
“index” is the current iteration.
“array” is the array itself.
=> Inline cond-else
var x = cond true 25 else 40 // like ternary operator
Here's the Python code.
print('Stripes early alpha release, Aug 9 2020') print('(c) All rights reserved') print('Type --syntax, --docs or --errors for info.') lines = [] finalProgram = [] indent = 0 tokens = [ '+', '-', '*', '/', '(', ')', '"', '=', '.' ] boolOps = [ '==', '>=', '<=', '!=' ] otherBoolOps = [ '>', '<' ] keywords = ['loop', 'cond', 'function', 'constructor', 'while', 'this', 'class', 'extends', 'integrate', 'var', 'array', 'foreach', 'switch', 'case', 'return', 'else', 'declare', 'run'] undefined = ['import', 'if', 'True', 'False', 'None', 'elif', 'lambda', 'except', 'finally'] operations = ['+', '-', '*', '/', '%'] inString = False types = ['number', 'string', 'boolean'] blockSep = ['{', '}'] true = True false = False none = None switchVar = None toOpen = '' import random, sys, time runFromFiles = bool(int(input('Type commands dynamically in the Python Shell or run from file? 0 or 1 '))) if runFromFiles: toOpen = open(input('Enter file name ')).read() def createError(error, text): print('---------- Error auto-generated by Stripes programming language ----------') print('Uncaught '+error+':') print(text) def integrate(module): exec(module + ' = ' + module[0].upper() + module[1:] + '()') isIntegrated[modules.index(module)] = True class time: def wait(seconds): time.sleep(seconds) class rand: def integ(left, right): try: return random.randint(left, right) except: if type(left) is not int or type(right) is not int: createError('ValueError', 'Must be ints. You did not specify ints.') elif not isIntegrated[6]: createError('ModuleError', 'Did not import module "main"') else: createError('IncompleteArgumentsError', 'rand.integ takes two arguments \'left\' and \'right\'. You did not specify two arguments') class main: pass class scanner: def scan(message): return input(message) def var(name, value): if [name, value] in variables: variables[variables.index([name, value])][1] = value else: variables.append([name, value]) def write(message): print(message) def lexer(string): global sofar global n # boolean operators => add regardless of spaces # keywords => add and move on # operations => add regardless of spaces # brackets => add separately # curly braces => add separately # other tokens => add regardless of spaces # everything else => add and move on as identifier lexed = [] # return sofar = '' # current token digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] inString = False for n in range(len(string)): # bool => (== > < != >= <=) if string[n] == '"' or string[n] == '\'': sofar += string[n] if inString: inString = False else: inString = True elif ((len(string) >= n+2 and string[n]+string[n+1] in boolOps)): sofar += string[n]+string[n+1] elif string[n] == '>' or string[n] == '<': sofar += string[n] elif string[n] in operations: sofar += string[n] elif string[n] in blockSep: sofar = '' lexed.append(['<separator>', string[n]]) elif string[n] == '(' or string[n] == ')': if sofar == '\n': lexed.append(['<newline>', 'newline']) else: if sofar in tokens or sofar in boolOps: lexed.append(['<token>', sofar]) else: lexed.append(['<identifier>', sofar]) sofar = '' lexed.append(['<token>', string[n]]) elif sofar in keywords: lexed.append(['<keyword>', sofar]) sofar = '' elif string[n] == '=' and not string[n]+string[n+1] == '==' and not string[n]+string[n-1] == '==': lexed.append(['<token>', '=']) sofar = '' elif string[n] in tokens and not string[n]+string[n-1] in boolOps: sofar += string[n] elif string[n] in digits: sofar += string[n] elif string[n] == ' ' and (len(sofar) > 2 and not sofar[-2]+sofar[-1] in boolOps or len(sofar) < 3) and not inString: if sofar == '\n': lexed.append(['<newline>', 'newline']) else: if sofar in tokens or sofar in boolOps: lexed.append(['<token>', sofar]) else: lexed.append(['<identifier>', sofar]) sofar = '' elif string[n] == ' ' and inString: sofar += string[n] elif n == len(string)-1: pass else: if not string[n]+string[n-1] in boolOps: sofar += string[n] if True: if True: if sofar == '\n': lexed.append(['<newline>', 'newline']) else: if sofar in tokens or sofar in boolOps: lexed.append(['<token>', sofar]) else: lexed.append(['<identifier>', sofar]) for array in lexed: if array[1] == '': del array[1] del array[0] for n in range(lexed.count([])): lexed.remove([]) if runFromFiles: index = 0 for array in lexed: if array[1][0] == '\n': array[1] = array[1][1:] if array[1] in keywords: array[0] = '<keyword>' lexed.insert(index, ['<newline>', 'newline']) else: for a in range(len(array[1])-1): if array[1][a] == '\n': lexed.insert(index+1, ['<newline>', 'newline']) stringBefore = array[1][0:a] stringAfter = array[1][a+1:] array[1] = stringBefore if array[1] in keywords: array[0] = '<keyword>' if stringAfter in keywords: lexed.insert(index+2, ['<keyword>', stringAfter]) else: lexed.insert(index+2, ['<identifier>', stringAfter]) break index += 1 return lexed def strNum(string): if len(string) == 0 or string is None: return False dots = 0 minus = 0 digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] for i in range(len(string)): if string[i] == '.': if dots == 0: dots += 1 else: return False elif string[i] == '-': if minus == 0: minus += 1 else: return False elif not string[i] in digits: return False return True def coerce(newType, obj): if not newType in types: createError('ValueError', 'Attempting to coerce into non-existant type') return else: if newType == 'number': if obj == false or obj == none: return 0 elif obj == none: return 1 else: try: return int(obj) except: createError('ValueError', 'Coercion into type "number" failed.') elif newType == 'string': if type(obj) == str: return obj elif type(obj) == bool: if obj == true: return "true" else: return "false" else: try: return str(obj) except: createError('ValueError', 'Coercion into type "string" failed.') elif newType == 'boolean': if obj == 0 or obj == "" or obj == "false" or obj == "none": return false else: return true def generateProgram(finalArr): global program global l global indent global switchVar global toRun program = '' toRun = True for array in finalArr: l = 0 while l < len(array): comp = array[l] if comp[1] == '//': program += indent*' '+'# ' elif comp[0] == '<identifier>': program += indent*' '+comp[1] elif comp[1] == '}': indent -= 4 elif comp[0] == '<token>': if comp[1] in operations or comp[1] == '(' or comp[1] == ')' or comp[1] == '"' or comp[1] == '=': program += indent*' '+comp[1] elif comp[1] == 'var': # (var) (name) (=) (value) if not array[l+1][1] in keywords: if not array[l+2][1] == '=': createError('InvalidExpressionError', 'Variable expression is var name = value') toRun = False else: if array[l+3][1] != 'cond': if array[l+4][1] != '(': program += indent*' '+array[l+1][1] + ' = ' + array[l+3][1] + '\n' # iden # iden # var # name # = # value # l = 3 => delete l => l = 2. # [var, x, =, 1] if l == 0: del array[l+3] # [var, x, =] => l = 0 del array[l+2] del array[l+1] del array[l] elif l == 1: # needs to delete item 2, 3, 4, 5 del array[l+3] del array[l+2] del array[l+1] del array[l] elif l == 2: del array[l+3] # [var, x, =] => l = 0 del array[l+2] del array[l+1] del array[l] elif l == 3: # needs to delete item 2, 3, 4, 5 del array[l+3] del array[l+2] del array[l+1] del array[l] else: del array[l+3] del array[l+2] del array[l+1] del array[l] else: program += indent*' '+array[l+1][1]+'='+array[l+3][1]+array[l+4][1]+array[l+5][1]+array[l+6][1]+'\n' del array[l+6] del array[l+5] del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] else: # var l # name l+1 # = l+2 # cond l+3 # condition l+4 # val1 l+5 # else l+6 # val2 l+7 try: testing = array[l+7] toCont = True except IndexError: toCont = False toRun = False createError('InvalidExpressionError', 'Inline cond-else variable expression is var name = cond condition val1 else val2') if toCont: program += indent*' '+'if '+array[l+4][1]+':\n'+indent*' '+' '+array[l+1][1]+' = '+array[l+5][1]+'\n'+indent*' '+'else:\n'+indent*' '+' '+array[l+1][1]+' = '+array[l+7][1]+'\n' del array[l+7] del array[l+6] del array[l+5] del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] else: createError('BuiltInOverwriteError', 'Cannot assign variable name as keyword') toRun = False elif comp[1] == 'array': if not array[l+1][0] in keywords: program += indent*' '+array[l+1][1] + ' = []\n' del array[l+1] del array[l] else: createError('BuiltInOverwriteError', 'Cannot assign array name as keyword') toRun = False elif comp[1] == 'function': # function l # name l+1 # ( l+2 # parameter (if no parameter => none) l+3 # ) l+4 # { l+5 ############ # Class functions # function (l) class (l+1) name (l+2) ( (l+3) params (l+4) ) (l+5) { (l+6) if array[l+1][1] != 'class': if array[l+5][1] != '{': createError('InvalidExpressionError', 'Function expression is function name ( param ) {') toRun = False else: if array[l+4][1] != ')': createError('InvalidExpressionError', 'Function expression is function name ( param ) {') toRun = False elif array[l+2][1] != '(': createError('InvalidExpressionError', 'Function expression is function name ( param ) {') toRun = False else: if array[l+3][1] == "none": param = '' else: param = array[l+3][1] program += indent*' '+'def '+array[l+1][1]+'('+param+'):\n'+indent*' ' del array[l+5] del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] indent += 4 else: if array[l+6][1] != '{' or array[l+5][1] != ')' or array[l+3][1] != '(': createError('InvalidExpressionError', 'Class function expression is function class name ( param ) {') toRun = False else: if array[l+4][1] == "none": param = 'self' else: param = 'self, '+array[l+4][1] if array[l+2][1] == "constructor": funcname = '__init__' else: funcname = array[l+2][1] program += indent*' '+'def '+funcname+'('+param+'):\n' indent += 4 del array[l+6] del array[l+5] del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] elif comp[1] == 'loop': if array[l+2][1] == 'iden': program += indent*' '+'for '+array[l+3][1]+' in range('+array[l+1][1]+'):\n' indent += 4 del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] elif array[l+2][1] == '{': program += indent*' '+'for _ in range('+array[l+1][1]+'):\n' indent += 4 del array[l+2] del array[l+1] del array[l] else: toRun = False createError('InvalidExpressionError', 'Could not identify loop type "repeat". Use either loop iter iden name { or loop iter {.') elif comp[1] == 'while': if array[l+2][1] == '{': program += indent*' '+'while '+array[l+1][1]+':\n' indent += 4 del array[l+2] del array[l+1] del array[l] elif array[l+2][1] == 'do': if array[l+3][1] == '{': program += indent*' '+'reservedForDoWhile = 0\n' program += indent*' '+'while True:\n' indent += 4 program += indent*' '+'reservedForDoWhile += 1\n' program += indent*' '+'if reservedForDoWhile > 1 and not '+array[l+1][1]+':\n' program += indent*' '+' break\n' del array[l+3] del array[l+2] del array[l+1] del array[l] elif strNum(array[l+3][1]): program += indent*' '+'reservedForDoWhile = 0\n' program += indent*' '+'while True:\n' indent += 4 program += indent*' '+'reservedForDoWhile += 1\n' program += indent*' '+'if reservedForDoWhile > '+array[l+3][1]+' and not '+array[l+1][1]+':\n' program += indent*' '+' break\n' del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] else: toRun = False createError('InvalidExpressionError', 'Could not identify loop type "dowhile". Use while cond do { or while cond do x{.') else: toRun = False createError('InvalidExpressionError', 'Could not identify loop type "while". Use while cond {, while cond do {, or while cond do x {.') elif comp[1] == 'cond': if array[l+2][1] == '{': program += indent*' '+'if '+array[l+1][1]+':\n' indent += 4 del array[l+2] del array[l+1] del array[l] else: # l l+1 l+2 if array[l+2][1] != '(' and array[l+2][1] != '[': toRun = False createError('InvalidExpressionError', 'Expression "cond" is cond condition {') else: if True: program += indent*' '+'if '+array[l+1][1]+array[l+2][1]+array[l+3][1]+array[l+4][1]+':\n' indent += 4 del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] elif comp[1] == 'else': if array[l+1][1] == '{': program += indent*' '+'else:\n' indent += 4 del array[l+1] del array[l] else: toRun = False createError('InvalidExpressionError', 'Expression "else" is else {') elif comp[1] == 'foreach': # foreach l # current l+1 # index l+2 # array l+3 # in l+4 # list l+5 # { l+6 try: testing = array[l+6] toCont = True except IndexError: toRun = False toCont = False createError('InvalidExpressionError', 'Expression "foreach" is foreach current index array in list {') if toCont: program += indent*' '+'for '+array[l+1][1]+' in '+array[l+5][1]+':\n' indent += 4 program += indent*' '+array[l+2][1]+' = '+array[l+5][1]+'.index('+array[l+1][1]+')\n' program += indent*' '+array[l+3][1]+' = '+array[l+5][1]+'\n' del array[l+6] del array[l+5] del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] elif comp[1] == 'switch': try: testing = array[l+2] toCont = True except IndexError: toRun = False toCont = False createError('InvalidExpressionError', 'Expression "switch" is switch var {') if toCont: switchVar = array[l+1][1] program += indent*' '+'switchVar = '+array[l+1][1]+'\n' program += indent*' '+'if True:\n' indent += 4 program += indent*' '+'if False:\n'+indent*' '+' pass\n' del array[l+2] del array[l+1] del array[l] elif comp[1] == 'case': try: testing = array[l+2] toCont = True except IndexError: toRun = False toCont = False createError('InvalidExpressionError', 'Expression "case" is case exp {') if toCont: if switchVar is None: toRun = False createError('InvalidPositionError', '"case" must be inside a switch statement') else: if array[l+1][1] == 'default': program += indent*' '+'else:\n' else: program += indent*' '+'elif switchVar == '+array[l+1][1]+':\n' indent += 4 del array[l+2] del array[l+1] del array[l] elif comp[1] == 'class': if array[l+2][1] != '{': # class name extends parent if array[l+4][1] != '{': toRun = False createError('InvalidExpressionError', 'Expression "class" is class name { or class name extends parent {') elif array[l+2][1] == 'extends': program += indent*' '+'class '+array[l+1][1]+'('+array[l+3][1]+'):\n' indent += 4 del array[l+4] del array[l+3] del array[l+2] del array[l+1] del array[l] else: toRun = False createError('InvalidExpressionError', 'Expression "class" is class name { or class name extends parent {') else: program += indent*' '+'class '+array[l+1][1]+':\n' indent += 4 del array[l+2] del array[l+1] del array[l] elif comp[1] == 'this': if len(array) > l+3 and array[l+2][1] != '=': program += indent*' '+'self.'+array[l+1][1]+'\n' del array[l+1] del array[l] else: program += indent*' '+'self.'+array[l+1][1]+' = '+array[l+3][1]+'\n' del array[l+3] del array[l+2] del array[l+1] del array[l] elif comp[1] == 'declare': # declare instance class program += indent*' '+array[l+1][1]+' = '+array[l+2][1]+'\n' del array[l+2] del array[l+1] del array[l] elif comp[1] == 'run': program += indent*' '+'file = open('+array[l+1][1]+')\n' program += indent*' '+'generateProgram(parseStepTwo(parseStepOne(file.read())))\n' del array[l+1] del array[l] elif comp[0] == '<keyword>': program += indent*' '+comp[1] elif comp[0] == '<newline>': program += indent*' '+'\n' l += 1 try: if toRun: """ print('------- Your program in Python -------') print(program) print('------- Result of program run: -------') """ exec(program) print("Program finished successfully") except AttributeError as e: createError('ValueError', str(e)) except TypeError as e: if str(e).startswith('unsupported operand') and str(e).endswith("'int' and 'str'"): createError('ValueError', 'Cannot perform \'operation\' on types \'number\' and \'string\'') elif str(e).startswith('unsupported operand') and str(e).endswith("'float' and 'str'"): createError('ValueError', 'Cannot perform \'operation\' on types \'number\' and \'string\'') elif str(e).endswith('given') or str(e).endswith('given)'): createError('IncompleteArgumentsError', 'Number of arguments specified does not match with function definition') elif str(e).startswith('object') and str(e).endswith('parameters'): createError('IncompleteArgumentsError', 'Number of arguments specified does not match with class constructor definition ( or there is no constructor )') except NameError as e: extract = '' pastQuote = False add = False firstQuote = False for n in range(len(str(e))): if add: extract += str(e)[n] if not pastQuote and str(e)[n] == "'" and not firstQuote: add = True firstQuote = True elif str(e)[n] == "'" and firstQuote: pastQuote = True add = False extract = extract[:-1] createError('ReferenceError', 'Undefined name - '+extract) except SyntaxError: createError('ParsingError', 'Invalid syntax') except IndexError: createError('ParsingError', 'Make sure to put spaces between identifiers in expressions like loop, while, var and function.') indentation = 0 if not runFromFiles: while True: line = input(indentation * ' ') if line == '': run = True break else: if line.endswith('{'): indentation += 4 elif line.endswith('}'): indentation -= 4 line += ' \n ' lines.append(line) x = 0 for item in lines: finalProgram.append(lexer(item)) x += 1 if run: generateProgram(finalProgram) else: generateProgram([lexer(toOpen)])
Last edited by scratch978654 (Aug. 20, 2020 06:51:23)
I will be temporarily participating on this site on April 1 2021 then leaving again
- scratch978654
- Scratcher
100+ posts
(first post in TIMAC) I made a programming language in Python (the non-conventional way)
Bump
I will be temporarily participating on this site on April 1 2021 then leaving again
- scratch978654
- Scratcher
100+ posts
(first post in TIMAC) I made a programming language in Python (the non-conventional way)
Bump why is no one responding
I will be temporarily participating on this site on April 1 2021 then leaving again
- Discussion Forums
- » Things I'm Making and Creating
- » (first post in TIMAC) I made a programming language in Python (the non-conventional way)