Compare commits

...

8 Commits

Author SHA1 Message Date
Johan B.W. de Vries
c318c780da Start on llvm IR target 2025-05-03 16:54:10 +02:00
Johan B.W. de Vries
1b8333fcfa Switch to Python 3.12 2025-05-03 16:51:38 +02:00
Johan B.W. de Vries
2eaa763a2c Remove skipchar, add stdinlineno / stdincolno
With the new check function, this helper method doesn't make
much sense to put in the standard library.

To replicate the same result, we do need to expose the current
line number; adding column number is a nice bonus.

Also; made it a bit clearer when a check failes in it2.

Also; the builtincheckfalse was not working.

Also; separated out the test input file to have more data.
2025-02-09 15:38:02 +01:00
Johan B.W. de Vries
ed09d37213 Fix: predeclared functions would sometimes fail 2025-02-09 15:03:47 +01:00
Johan B.W. de Vries
a184d4c5e3 You can now return a constant string
Rather than having to store it as a variable
2025-02-09 15:03:11 +01:00
Johan B.W. de Vries
5fa042c5cd Adds check builtin 2025-02-09 15:02:25 +01:00
Johan B.W. de Vries
931a861fef Made it clearer where in a Python build it failed
Also, fixed a newline.
2025-02-09 15:01:01 +01:00
Johan B.W. de Vries
7568a15093 Makefile improvements
Using a tmp file prevents a failed build from being reported
as succeeded.

Changes in the compiler should now rebuild the tests
automatically.
2025-02-09 14:59:09 +01:00
31 changed files with 1167 additions and 146 deletions

View File

@ -1,6 +1,6 @@
.SUFFIXES: .SUFFIXES:
PYVERSION=3.10 PYVERSION=3.12
PYPREFIX=/usr PYPREFIX=/usr
INCLUDES=-I$(PYPREFIX)/include/python$(PYVERSION) INCLUDES=-I$(PYPREFIX)/include/python$(PYVERSION)
CYTHON=cython3 CYTHON=cython3

View File

@ -3,6 +3,7 @@ import sys
def emit(string): def emit(string):
sys.stdout.write(string) sys.stdout.write(string)
sys.stdout.flush()
def trace(header, value): def trace(header, value):
if os.environ.get('TRACE'): if os.environ.get('TRACE'):
@ -38,11 +39,6 @@ def skip():
PEEK = None PEEK = None
def skipchar(char):
global LINE
assert char == peek(), (LINE, char, peek())
skip()
def emitln(data): def emitln(data):
emit(data) emit(data)
emit(eol) emit(eol)
@ -60,6 +56,11 @@ def lexident():
return word return word
def skipchar(char):
global LINE
assert char == peek(), (LINE, char, peek())
skip()
def parseconststring(): def parseconststring():
skipchar(quote) skipchar(quote)
emit(quote) emit(quote)
@ -161,10 +162,60 @@ def parsestatreturn(indent):
emit('return ') emit('return ')
if ' ' == peek(): if ' ' == peek():
skip() skip()
parseexprvarref() if quote == peek():
parseconststring()
else:
parseexprvarref()
emit(eol) emit(eol)
skipchar(eol) skipchar(eol)
def parsestatcheck(indent):
skipchar(' ')
emit(' ' * indent)
emit('assert ')
func_name = lexident()
emit(func_name)
emit('(')
notfirst = False
while True:
skipchar(' ')
if ':' == peek():
break
if notfirst:
emit(', ')
if quote == peek():
parseconststring()
else:
parseexprvarref()
notfirst = True
skipchar(':')
emit('), (')
notfirst = False
while True:
skipchar(' ')
if notfirst:
emit(', ')
if quote == peek():
parseconststring()
else:
parseexprvarref()
if eol == peek():
break
notfirst = True
emitln(')')
def parsestattrace(indent): def parsestattrace(indent):
skipchar(' ') skipchar(' ')
emit(' ' * indent) emit(' ' * indent)
@ -210,6 +261,10 @@ def parsestat(indent):
parsestatreturn(indent) parsestatreturn(indent)
return return
if call == "check":
parsestatcheck(indent)
return
if call == "trace": if call == "trace":
parsestattrace(indent) parsestattrace(indent)
return return
@ -300,6 +355,7 @@ def emitheader():
emitln("") emitln("")
emitln("def emit(string):") emitln("def emit(string):")
emitln(" sys.stdout.write(string)") emitln(" sys.stdout.write(string)")
emitln(" sys.stdout.flush()")
emitln("") emitln("")
emitln("def trace(header, value):") emitln("def trace(header, value):")
emitln(" if os.environ.get('TRACE'):") emitln(" if os.environ.get('TRACE'):")
@ -308,33 +364,40 @@ def emitheader():
emitln("eof = chr(0)") emitln("eof = chr(0)")
emitln("eol = chr(10)") emitln("eol = chr(10)")
emitln("quote = chr(34)") emitln("quote = chr(34)")
emitln("PEEK = None") emitln("")
emitln("LINE = 1") emitln("STDINCOLNO = 0")
emitln("STDINLINENO = 1")
emitln("STDINPEEK = None")
emitln("")
emitln("def _readchar():")
emitln(" char = sys.stdin.read(1)")
emitln(" trace('char', char)")
emitln(" if not char:")
emitln(" return eof")
emitln(" return char")
emitln("") emitln("")
emitln("def peek():") emitln("def peek():")
emitln(" global PEEK") emitln(" return STDINPEEK")
emitln(" if PEEK is None:")
emitln(" char = sys.stdin.read(1)")
emitln(" trace('char', char)")
emitln(" if not char:")
emitln(" PEEK = eof")
emitln(" else:")
emitln(" PEEK = char")
emitln(" return PEEK")
emitln("")
emitln("peek()")
emitln("") emitln("")
emitln("def skip():") emitln("def skip():")
emitln(" global LINE") emitln(" global STDINCOLNO")
emitln(" global PEEK") emitln(" global STDINLINENO")
emitln(" if eol == PEEK:") emitln(" global STDINPEEK")
emitln(" LINE += 1") emitln(" if eol == STDINPEEK:")
emitln(" PEEK = None") emitln(" STDINLINENO += 1")
emitln(" STDINCOLNO = 0")
emitln(" STDINCOLNO += 1")
emitln(" STDINPEEK = _readchar()")
emitln("") emitln("")
emitln("def skipchar(char):") emitln("def stdinlineno():")
emitln(" global LINE") emitln(" global STDINLINENO")
emitln(" assert char == peek(), (LINE, char, peek())") emitln(" return str(STDINLINENO)")
emitln(" skip()") emitln("")
emitln("def stdincolno():")
emitln(" global STDINCOLNO")
emitln(" return str(STDINCOLNO)")
emitln("")
emitln("skip()")
emitln("") emitln("")
def emitfooter(): def emitfooter():

View File

@ -2,3 +2,4 @@
/lang0py*.exe /lang0py*.exe
/lang0py*.o /lang0py*.o
/lang0py*.py /lang0py*.py
/lang0py*.py.tmp

View File

@ -1,6 +1,6 @@
.SUFFIXES: .SUFFIXES:
PYVERSION=3.10 PYVERSION=3.12
PYPREFIX=/usr PYPREFIX=/usr
INCLUDES=-I$(PYPREFIX)/include/python$(PYVERSION) INCLUDES=-I$(PYPREFIX)/include/python$(PYVERSION)
CYTHON=cython3 CYTHON=cython3
@ -18,18 +18,22 @@ all: lang0py.exe
$(CYTHON) -3 --embed $< $(CYTHON) -3 --embed $<
lang0py0.py: lang0py.lang0 $(LANG0PY) lang0py0.py: lang0py.lang0 $(LANG0PY)
cat $< | $(LANG0PY) > $@ cat $< | $(LANG0PY) > $@.tmp
mv $@.tmp $@
lang0py1.py: lang0py.lang0 lang0py0.exe lang0py1.py: lang0py.lang0 lang0py0.exe
cat $< | ./lang0py0.exe > $@ cat $< | ./lang0py0.exe > $@.tmp
mv $@.tmp $@
# Cannot diff on the first iteration - platform change # Cannot diff on the first iteration - platform change
lang0py2.py: lang0py.lang0 lang0py1.exe lang0py2.py: lang0py.lang0 lang0py1.exe
cat $< | ./lang0py1.exe > $@ cat $< | ./lang0py1.exe > $@.tmp
mv $@.tmp $@
-diff lang0py1.py lang0py2.py -diff lang0py1.py lang0py2.py
lang0py.py: lang0py.lang0 lang0py2.exe lang0py.py: lang0py.lang0 lang0py2.exe
cat $< | ./lang0py2.exe > $@ cat $< | ./lang0py2.exe > $@.tmp
mv $@.tmp $@
-diff lang0py2.py lang0py.py -diff lang0py2.py lang0py.py
clean: clean:

View File

@ -1,3 +1,14 @@
skipchar exp:
declare act
declare lineno
declare colno
calc act peek
calc lineno stdinlineno
calc colno stdincolno
check eq exp act : "Unexpected character" act "expected" exp "at" lineno colno
skip
/
emitln data: emitln data:
emit data emit data
emit eol emit eol
@ -151,12 +162,96 @@ parsestatreturn indent:
calc isspace eq char " " calc isspace eq char " "
if isspace if isspace
skip skip
parseexprvarref calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
/ /
emit eol emit eol
skipchar eol skipchar eol
/ /
parsestatcheck indent:
declare char
declare funcname
declare iscolon
declare isnotquote
declare isquote
declare notfirst
skipchar " "
emit indent
emit "assert "
calc funcname lexident
emit funcname
emit "("
set notfirst ""
forever
skipchar " "
calc char peek
calc iscolon eq char ":"
if iscolon
break
/
if notfirst
emit ", "
/
calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
set notfirst "1"
/
skipchar ":"
emit "), ("
set notfirst ""
forever
skipchar " "
if notfirst
emit ", "
/
calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
calc char peek
calc iseol eq char eol
if iseol
break
/
set notfirst "1"
/
emitln ")"
/
parsestattrace indent: parsestattrace indent:
emit indent emit indent
emit "trace(" emit "trace("
@ -214,6 +309,11 @@ parsestat indent:
parsestattrace indent parsestattrace indent
return return
/ /
calc ischeck eq call "check"
if ischeck
parsestatcheck indent
return
/
emit indent emit indent
emit call emit call
emit "(" emit "("
@ -280,16 +380,11 @@ parseblock indent:
parsefunc: parsefunc:
calc funcname lexident calc funcname lexident
calc char peek
calc iseoblock eq char "/"
if iseoblock
return
/
trace funcname trace funcname
emit "def " emit "def "
emit funcname emit funcname
emit "(" emit "("
set first "1" set isnotfirst ""
forever forever
calc char peek calc char peek
calc isspace eq char " " calc isspace eq char " "
@ -299,13 +394,11 @@ parsefunc:
/ /
skip skip
calc var lexident calc var lexident
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst if isnotfirst
emit ", " emit ", "
/ /
emit var emit var
set first "0" set isnotfirst "1"
/ /
calc char peek calc char peek
calc iseoblock eq char "/" calc iseoblock eq char "/"
@ -339,41 +432,49 @@ emitheader:
emitln "" emitln ""
emitln "def emit(string):" emitln "def emit(string):"
emitln " sys.stdout.write(string)" emitln " sys.stdout.write(string)"
emitln " sys.stdout.flush()"
emitln "" emitln ""
emitln "def trace(header, value):" emitln "def trace(header, value):"
emitln " if os.environ.get('TRACE'):" emitln " if os.environ.get('TRACE'):"
emitln " sys.stderr.write(f'{header}={value!r}\')" emitln " sys.stderr.write(f'{header}={value!r}\\n')"
emitln "" emitln ""
emitln "eof = chr(0)" emitln "eof = chr(0)"
emitln "eol = chr(10)" emitln "eol = chr(10)"
emitln "quote = chr(34)" emitln "quote = chr(34)"
emitln "PEEK = None" emitln ""
emitln "LINE = 1" emitln "STDINCOLNO = 0"
emitln "STDINLINENO = 1"
emitln "STDINPEEK = None"
emitln ""
emitln "def _readchar():"
emitln " char = sys.stdin.read(1)"
emitln " trace('char', char)"
emitln " if not char:"
emitln " return eof"
emitln " return char"
emitln "" emitln ""
emitln "def peek():" emitln "def peek():"
emitln " global PEEK" emitln " return STDINPEEK"
emitln " if PEEK is None:"
emitln " char = sys.stdin.read(1)"
emitln " trace('char', char)"
emitln " if not char:"
emitln " PEEK = eof"
emitln " else:"
emitln " PEEK = char"
emitln " return PEEK"
emitln ""
emitln "peek()"
emitln "" emitln ""
emitln "def skip():" emitln "def skip():"
emitln " global LINE" emitln " global STDINCOLNO"
emitln " global PEEK" emitln " global STDINLINENO"
emitln " if eol == PEEK:" emitln " global STDINPEEK"
emitln " LINE += 1" emitln " if eol == STDINPEEK:"
emitln " PEEK = None" emitln " STDINLINENO += 1"
emitln " STDINCOLNO = 0"
emitln " STDINCOLNO += 1"
emitln " STDINPEEK = _readchar()"
emitln "" emitln ""
emitln "def skipchar(char):" emitln "def stdinlineno():"
emitln " global LINE" emitln " global STDINLINENO"
emitln " assert char == peek(), (LINE, char, peek())" emitln " return str(STDINLINENO)"
emitln " skip()" emitln ""
emitln "def stdincolno():"
emitln " global STDINCOLNO"
emitln " return str(STDINCOLNO)"
emitln ""
emitln "skip()"
emitln "" emitln ""
/ /

1
2-lang0c/.gitignore vendored
View File

@ -1,4 +1,5 @@
/lang0c*.c /lang0c*.c
/lang0c*.c.tmp
/lang0c*.exe /lang0c*.exe
/lang0c*.o /lang0c*.o
/lang0c*.py /lang0c*.py

View File

@ -1,6 +1,6 @@
.SUFFIXES: .SUFFIXES:
PYVERSION=3.10 PYVERSION=3.12
PYPREFIX=/usr PYPREFIX=/usr
INCLUDES= INCLUDES=
CYTHON=cython3 CYTHON=cython3
@ -15,7 +15,8 @@ all: lang0c.exe
gcc -c $< gcc -c $<
lang0c0.py: lang0c.lang0 $(LANG0PY) lang0c0.py: lang0c.lang0 $(LANG0PY)
cat $< | $(LANG0PY) > $@ cat $< | $(LANG0PY) > $@.tmp
mv $@.tmp $@
lang0c0.c: lang0c0.py lang0c0.c: lang0c0.py
$(CYTHON) -3 --embed $< $(CYTHON) -3 --embed $<
@ -27,15 +28,18 @@ lang0c0.exe: lang0c0.o
gcc -o $@ $< -lpython$(PYVERSION) gcc -o $@ $< -lpython$(PYVERSION)
lang0c1.c: lang0c.lang0 lang0c0.exe lang0c1.c: lang0c.lang0 lang0c0.exe
cat $< | ./lang0c0.exe > $@ cat $< | ./lang0c0.exe > $@.tmp
mv $@.tmp $@
# Cannot diff on the first iteration - platform change # Cannot diff on the first iteration - platform change
lang0c2.c: lang0c.lang0 lang0c1.exe lang0c2.c: lang0c.lang0 lang0c1.exe
cat $< | ./lang0c1.exe > $@ cat $< | ./lang0c1.exe > $@.tmp
mv $@.tmp $@
-diff lang0c1.c lang0c2.c -diff lang0c1.c lang0c2.c
lang0c.c: lang0c.lang0 lang0c2.exe lang0c.c: lang0c.lang0 lang0c2.exe
cat $< | ./lang0c2.exe > $@ cat $< | ./lang0c2.exe > $@.tmp
mv $@.tmp $@
-diff lang0c2.c lang0c.c -diff lang0c2.c lang0c.c
clean: clean:

View File

@ -1,3 +1,14 @@
skipchar exp:
declare act
declare lineno
declare colno
calc act peek
calc lineno stdinlineno
calc colno stdincolno
check eq exp act : "Unexpected character" act "expected" exp "at" lineno colno
skip
/
emitln data: emitln data:
emit data emit data
emit eol emit eol
@ -195,6 +206,8 @@ parsestatbreak indent:
parsestatreturn indent: parsestatreturn indent:
declare char declare char
declare isspace declare isspace
declare isnotquote
declare isquote
declare isnotspace declare isnotspace
emit indent emit indent
emit "return " emit "return "
@ -203,7 +216,15 @@ parsestatreturn indent:
calc isnotspace not isspace calc isnotspace not isspace
if isspace if isspace
skip skip
parseexprvarref calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
/ /
if isnotspace if isnotspace
emit "0" emit "0"
@ -214,6 +235,98 @@ parsestatreturn indent:
return return
/ /
parsestatcheck indent:
declare char
declare funcname
declare iscolon
declare iseol
declare isnotquote
declare isquote
declare notfirst
skipchar " "
emit indent
emit "if( 0 == strlen("
calc funcname lexident
emit funcname
emit "("
set notfirst ""
forever
skipchar " "
calc char peek
calc iscolon eq char ":"
if iscolon
break
/
if notfirst
emit ", "
/
calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
set notfirst "1"
/
skipchar ":"
emitln ")) )"
emit indent
emitln "{"
emit " fprintf(stderr, "
emit quote
emit "%s"
emit quote
emit ", "
emit quote
emit "ERROR:"
emit quote
emitln ");"
forever
skipchar " "
emit indent
emit " fprintf(stderr, "
emit quote
emit " %s"
emit quote
emit ", "
calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
emitln ");"
calc char peek
calc iseol eq char eol
if iseol
break
/
/
emit indent
emitln " exit(1);"
emit indent
emitln "}"
/
parsestattrace indent: parsestattrace indent:
declare varname declare varname
emit indent emit indent
@ -278,6 +391,11 @@ parsestat indent:
parsestatreturn indent parsestatreturn indent
return return
/ /
calc iscall eq call "check"
if iscall
parsestatcheck indent
return
/
calc iscall eq call "trace" calc iscall eq call "trace"
if iscall if iscall
parsestattrace indent parsestattrace indent
@ -471,35 +589,55 @@ emitheader:
emitln " return 0;" emitln " return 0;"
emitln "}" emitln "}"
emitln "" emitln ""
emitln "int LINE = 1;" emitln "int STDINCOLNO = 1;"
emitln "int STDINLINENO = 1;"
emitln "char * STDINPEEK = 0;"
emitln "" emitln ""
emitln "char * peek()" emitln "char * _readchar()"
emitln "{" emitln "{"
emitln " char * res = malloc(2*sizeof(char));" emitln " char * res = malloc(2*sizeof(char));"
emitln " res[0] = getc(stdin);" emitln " res[0] = getc(stdin);"
emitln " res[1] = 0;" emitln " res[1] = 0;"
emitln " ungetc(res[0], stdin);"
emitln " return res;" emitln " return res;"
emitln "}" emitln "}"
emitln "" emitln ""
emitln "char * peek()"
emitln "{"
emitln " if( !STDINPEEK ) STDINPEEK = _readchar(); // First byte read"
emitln " return STDINPEEK;"
emitln "}"
emitln ""
emitln "void skip()" emitln "void skip()"
emitln "{" emitln "{"
emitln " char c = getc(stdin);" emitln " if( !STDINPEEK ) STDINPEEK = _readchar(); // First byte read, not even peek()'d"
emitln " if( c == 10 ) LINE += 1;" emitln " if( STDINPEEK[0] == 10 ) {"
emitln " STDINLINENO += 1;"
emitln " STDINCOLNO = 0;"
emitln " }"
emitln " STDINCOLNO += 1;"
emitln " STDINPEEK = _readchar();"
emitln "}" emitln "}"
emitln "" emitln ""
emitln "void skipchar(char * chr)" emitln "char * stdinlineno()"
emitln "{" emitln "{"
emitln " assert(strlen(chr) == 1);" emitln " char * res = malloc(8*sizeof(char));"
emitln " char * act = peek();" emit " sprintf(res, "
emitln " assert(strlen(act) == 1);"
emitln " if( chr[0] == act[0] ) {skip(); return;};"
emit " fprintf(stderr, "
emit quote emit quote
emit "Expected '%c' on line %d but saw '%c' instead%c" emit "%d"
emit quote emit quote
emitln ", chr[0], LINE, act[0], 10);" emitln ", STDINLINENO);"
emitln " exit(1);" emitln " return res;"
emitln "}"
emitln ""
emitln "char * stdincolno()"
emitln "{"
emitln " char * res = malloc(8*sizeof(char));"
emit " sprintf(res, "
emit quote
emit "%d"
emit quote
emitln ", STDINCOLNO);"
emitln " return res;"
emitln "}" emitln "}"
emitln "" emitln ""
return return

6
3-lang0ll/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/lang0ll*.c
/lang0ll*.exe
/lang0ll*.ll
/lang0ll*.o
/foo.exe
/foo.ll

42
3-lang0ll/Makefile Normal file
View File

@ -0,0 +1,42 @@
.SUFFIXES:
LANG0C=$(CURDIR)/../2-lang0c/lang0c.exe
all: lang0ll.exe
%.exe: %.s
clang $^ -o $@
%.s: %.ll
llc --relocation-model=pic $^
lang0ll0.c: lang0ll.lang0 $(LANG0C)
cat $< | $(LANG0C) > $@
lang0ll0.o: lang0ll0.c
gcc -c $<
lang0ll0.exe: lang0ll0.o
gcc -o $@ $<
lang0ll1.ll: lang0ll.lang0 lang0ll0.exe
cat $< | ./lang0ll0.exe > $@
# Cannot diff on the first iteration - platform change
lang0ll2.ll: lang0ll.lang0 lang0ll1.exe
cat $< | ./lang0ll1.exe > $@
-diff lang0ll1.ll lang0ll2.ll
lang0ll.ll: lang0ll.lang0 lang0ll2.exe
cat $< | ./lang0ll2.exe > $@
-diff lang0ll2.ll lang0ll.ll
foo.ll: foo.c
clang -S -emit-llvm $^
clean:
rm -f lang0ll*.py lang0ll*.ll lang0ll*.o lang0ll*.exe foo.ll foo.o foo.exe

17
3-lang0ll/foo.c Normal file
View File

@ -0,0 +1,17 @@
void print(char * x)
{
return;
}
int sum(int a, int b)
{
return a + b;
}
int main()
{
char * foo = "Hello, world!";
print("This is a test.");
return sum(7, 15);
}

472
3-lang0ll/lang0ll.lang0 Normal file
View File

@ -0,0 +1,472 @@
increaseindent indent:
calc indent addstringchar indent " "
calc indent addstringchar indent " "
calc indent addstringchar indent " "
calc indent addstringchar indent " "
return indent
/
lexident:
declare char
declare isbeforea
declare isafterz
declare word
set word ""
forever
calc char peek
calc isbeforea lt char "a"
if isbeforea
break
/
calc isafterz lt "z" char
if isafterz
break
/
calc word addstringchar word char
skip
/
return word
/
parseconststring:
declare char
declare iseof
declare isquote
skipchar quote
forever
calc char peek
calc iseof eq char eof
if iseof
break
/
calc isquote eq char quote
if isquote
break
/
skip
/
skipchar quote
emit "i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0)"
return
/
parseexprvarref:
declare varname
calc varname lexident
emit "i8* %"
emit varname
return
/
parseexprcall:
declare char
declare first
declare funcname
declare isspace
declare isnotspace
declare isfirst
declare isnotfirst
declare isquote
declare isnotquote
calc funcname lexident
emit funcname
emit "("
set first "1"
forever
calc char peek
calc isspace eq char " "
calc isnotspace not isspace
if isnotspace
break
/
skip
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst
emit ", "
/
calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
set first "0"
/
emit ")"
return
/
parsestatdeclare indent:
declare var
skipchar " "
calc var lexident
emit indent
emit "char * var_"
emit var
emit ";"
emit eol
skipchar eol
return
/
parsestatset indent:
declare var
skipchar " "
calc var lexident
skipchar " "
emit indent
emit "var_"
emit var
emit " = "
parseconststring
emit ";"
emit eol
skipchar eol
return
/
parsestatcalc indent:
declare var
declare varidx
skipchar " "
calc var lexident
skipchar " "
emit indent
emit "%"
emit var
calc varidx mapgetkey "funcvarused" var
calc varidx intinc varidx
mapsetkey "funcvarused" var varidx
emit "."
emit varidx
emit " = call i8* @"
parseexprcall
emit ";"
emit eol
skipchar eol
return
/
parseblock indent/
parsestatif indent:
declare indentt
skipchar " "
emit indent
emit "if ( 0 < strlen("
parseexprvarref
emit ") )\n"
emit indent
emit "{\n"
skipchar eol
calc indentt increaseindent indent
parseblock indentt
emit indent
emit "}\n"
return
/
parsestatforever indent:
declare indentt
emit indent
emit "while (1)\n"
emit indent
emit "{\n"
skipchar eol
calc indentt increaseindent indent
parseblock indentt
emit indent
emit "}\n"
return
/
parsestatbreak indent:
emit indent
emit "break;\n"
skipchar eol
return
/
parsestatreturn indent:
declare char
declare isspace
declare isnotspace
emit indent
emit "ret "
calc char peek
calc isspace eq char " "
calc isnotspace not isspace
if isspace
skip
parseexprvarref
/
if isnotspace
emit "i18* 0"
/
emit ";"
emit eol
skipchar eol
return
/
parsestattrace indent:
declare varname
emit indent
emit "trace("
emit quote
skipchar " "
calc varname lexident
emit varname
emit quote
emit ", var_"
emit varname
emit ");\n"
skipchar eol
return
/
parsestat indent:
declare call
declare char
declare first
declare iscall
declare isfirst
declare isspace
declare isquote
declare isnotfirst
declare isnotspace
declare isnotquote
calc call lexident
trace call
calc iscall eq call "declare"
if iscall
parsestatdeclare indent
return
/
calc iscall eq call "set"
if iscall
parsestatset indent
return
/
calc iscall eq call "calc"
if iscall
parsestatcalc indent
return
/
calc iscall eq call "if"
if iscall
parsestatif indent
return
/
calc iscall eq call "forever"
if iscall
parsestatforever indent
return
/
calc iscall eq call "break"
if iscall
parsestatbreak indent
return
/
calc iscall eq call "return"
if iscall
parsestatreturn indent
return
/
calc iscall eq call "trace"
if iscall
parsestattrace indent
return
/
emit indent
emit call
emit "("
set first "1"
forever
calc char peek
calc isspace eq char " "
calc isnotspace not isspace
if isnotspace
break
/
skip
calc char peek
calc isquote eq char quote
calc isnotquote not isquote
if isquote
parseconststring
/
if isnotquote
parseexprvarref
/
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst
emit ", "
/
set first "0"
/
skipchar eol
emit ");\n"
return
/
parseblock indent:
declare char
declare copy
declare iseol
declare isnoteol
declare isdone
declare iseoblock
forever
forever
calc char peek
calc iseol eq char eol
calc isnoteol not iseol
if isnoteol
break
/
skip
/
set copy " "
forever
calc isdone eq copy indent
if isdone
break
/
skipchar "\t"
calc copy increaseindent copy
/
calc char peek
calc iseoblock eq char "/"
if iseoblock
skip
skipchar eol
break
/
skipchar "\t"
parsestat indent
/
return
/
parsefunc:
declare char
declare first
declare funcname
declare iseoblock
declare isfirst
declare isspace
declare isnotfirst
declare isnotspace
declare var
mapclear "funcvarused"
calc funcname lexident
trace funcname
emit "define i8* @"
emit funcname
emit "("
set first "1"
forever
calc char peek
calc isspace eq char " "
calc isnotspace not isspace
if isnotspace
break
/
skip
calc var lexident
calc isfirst eq first "1"
calc isnotfirst not isfirst
if isnotfirst
emit ", "
/
emit "i8* %"
emit var
mapsetkey "funcvarused" "var" "0"
emit "_0"
set first "0"
/
calc char peek
calc iseoblock eq char "/"
if iseoblock
skipchar "/"
skipchar eol
emit ");"
emit "\n"
return
/
skipchar ":"
skipchar eol
emit ")\n{\n"
parseblock " "
emit "}\n\n"
return
/
emitheader:
emit "target datalayout = "
emit quote
emit "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
emit quote
emit eol
emit "target triple = "
emit quote
emit "x86_64-pc-linux-gnu"
emit quote
emit eol
emit eol
emit "define i8* @addstringchar(i8* %0, i8* %1)"
emit eol
emit "{"
emit eol
emit " ; todo"
emit eol
emit " ret i8* %0;"
emit eol
emit "}"
emit eol
emit eol
return
/
emitfooter:
emit ""
return
/
main:
declare char
declare iseof
declare iseol
declare isnoteol
emitheader
forever
calc char peek
calc iseof eq char eof
if iseof
break
/
forever
calc char peek
calc iseol eq char eol
calc isnoteol not iseol
if isnoteol
break
/
skip
/
parsefunc
/
emitfooter
return
/

19
3-lang0ll/notes.ll Normal file
View File

@ -0,0 +1,19 @@
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"
define i8* @addstringchar(i8* %0, i8* %1)
{
; todo
ret i8* %0;
}
define i8* @increaseindent(i8* %indent)
{
%indent.1 = call i8* @addstringchar(i8* %indent, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0));
%indent.2 = call i8* @addstringchar(i8* %indent.1, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0));
%indent.3 = call i8* @addstringchar(i8* %indent.2, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0));
%indent.4 = call i8* @addstringchar(i8* %indent.3, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0));
ret i8* %indent.4;
}
@.str = private unnamed_addr constant [14 x i8] c"Hello, world!\00", align 1

View File

@ -113,6 +113,10 @@ Returns the given value. You can not give an argument if your function is never
### Builtins ### Builtins
#### check func [arg0..] : msg [arg0..]
Calls the given function with the given arguments. If the function's return value is not found to be true, outputs the given `msg` and halts the program.
#### trace #### trace
Writes the name and value of the variable passed to stderr if the TRACE environment variable is set. Writes the name and value of the variable passed to stderr if the TRACE environment variable is set.
@ -133,12 +137,44 @@ Writes the given string to standard output.
Return true if the given strings are the same. Return true if the given strings are the same.
#### intinc a
Available in it2 runtime and onwards.
Interprets string `a` as an integer, increases it by one and returns it (as a string).
If `a` cannot be interpreted as an integer, or has additional bytes, this function returns 1.
#### lt a b #### lt a b
`a` and `b` are expected to have length 1. `a` and `b` are expected to have length 1.
Return true if a would sort before b. Return true if a would sort before b.
#### mapclear mapname
Available in it2 runtime and onwards.
Maps are global and can be used from any function.
Clears all values set in the map named `mapname`.
#### mapgetkey mapname key
Available in it2 runtime and onwards.
Maps are global and can be used from any function.
Looks up `key` in the map named `mapname` and returns it.
If not found, returns an empty string.
#### mapsetkey mapname key value
Available in it2 runtime and onwards.
Maps are global and can be used from any function.
Adds a mapping from `key` to `value` to the map named `mapname`.
#### peek #### peek
Checks stdin for the next character and returns it. Checks stdin for the next character and returns it.
@ -147,9 +183,13 @@ Checks stdin for the next character and returns it.
Advances stdin a single character. Advances stdin a single character.
#### skipchar a #### stdincolno
Advances stdin a single character, if it matches the first character of `a`. Otherwise, exits the program. Returns the column number for stdin (starting at 1)
#### stdinlineno
Returns the line number for stdin (starting at 1)
### Typing ### Typing

View File

@ -1,29 +1,35 @@
.SUFFIXES: .SUFFIXES:
.PHONY: all clean .PHONY: all clean
.PRECIOUS: build/%.it0.py build/%.it0.c
.DELETE_ON_ERROR:
PYVERSION=3.10 PYVERSION=3.12
PYPREFIX=/usr PYPREFIX=/usr
CYTHON=cython3 CYTHON=cython3
CC=gcc CC=gcc
TESTLIST=$(shell ls *.lang0 | sed 's/.lang0//') # builtincheckfalse is separate since it's supposed to crash. It's a test for the test 'framework', if you will.
TESTLIST=$(shell ls *.lang0 | grep -v 'builtincheckfalse' | sed 's/.lang0//')
all: check all: check
check: all-it0.results all-it1.results all-it2.results check: all-it0.results all-it1.results all-it2.results
! grep -v 'Success' $^ ! grep -v 'Success' $^
all-it0.results: $(addprefix build/,$(addsuffix .it0, $(TESTLIST))) all-it0.results: $(addprefix build/,$(addsuffix .it0, $(TESTLIST))) build/builtincheckfalse.it0
-rm -f $@ -rm -f $@
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; echo "Hello" | ./build/$(test).it0 >> $@ ; echo "" >> $@ ;) cat test-input.txt | build/builtincheckfalse.it0 2> /dev/null 2>&1 | grep 'Success'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it0 >> $@ ; echo "" >> $@ ;)
all-it1.results: $(addprefix build/,$(addsuffix .it1, $(TESTLIST))) all-it1.results: $(addprefix build/,$(addsuffix .it1, $(TESTLIST))) build/builtincheckfalse.it1
-rm -f $@ -rm -f $@
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; echo "Hello" | ./build/$(test).it1 >> $@ ; echo "" >> $@ ;) cat test-input.txt | build/builtincheckfalse.it1 2> /dev/null 2>&1 | grep 'Success'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it1 >> $@ ; echo "" >> $@ ;)
all-it2.results: $(addprefix build/,$(addsuffix .it2, $(TESTLIST))) all-it2.results: $(addprefix build/,$(addsuffix .it2, $(TESTLIST))) build/builtincheckfalse.it2
-rm -f $@ -rm -f $@
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; echo "Hello" | ./build/$(test).it2 >> $@ ; echo "" >> $@ ;) cat test-input.txt | build/builtincheckfalse.it2 2> /dev/null 2>&1 | grep 'Success'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it2 >> $@ ; echo "" >> $@ ;)
clean: clean:
-rm -f *.results build/*.it0* build/*.it1* build/*.it2* -rm -f *.results build/*.it0* build/*.it1* build/*.it2*
@ -31,8 +37,12 @@ clean:
### ###
# it0 # it0
../0-lang0py/lang0py.exe: ../0-lang0py/lang0py.py
$(MAKE) -C ../0-lang0py
build/%.it0.py: %.lang0 ../0-lang0py/lang0py.exe build/%.it0.py: %.lang0 ../0-lang0py/lang0py.exe
cat $< | ../0-lang0py/lang0py.exe > $@ cat $< | ../0-lang0py/lang0py.exe > $@.tmp
mv $@.tmp $@
build/%.it0.c: build/%.it0.py build/%.it0.c: build/%.it0.py
$(CYTHON) -3 --embed -o $@ $^ $(CYTHON) -3 --embed -o $@ $^
@ -46,8 +56,12 @@ build/%.it0: build/%.it0.o
### ###
# it1 # it1
../1-lang0py/lang0py.exe: ../0-lang0py/lang0py.exe ../1-lang0py/lang0py.lang0
$(MAKE) -C ../1-lang0py
build/%.it1.py: %.lang0 ../1-lang0py/lang0py.exe build/%.it1.py: %.lang0 ../1-lang0py/lang0py.exe
cat $< | ../1-lang0py/lang0py.exe > $@ cat $< | ../1-lang0py/lang0py.exe > $@.tmp
mv $@.tmp $@
build/%.it1.c: build/%.it1.py build/%.it1.c: build/%.it1.py
$(CYTHON) -3 --embed -o $@ $^ $(CYTHON) -3 --embed -o $@ $^
@ -61,8 +75,12 @@ build/%.it1: build/%.it1.o
### ###
# it2 # it2
../2-lang0c/lang0c.exe: ../0-lang0py/lang0py.exe ../1-lang0py/lang0py.exe ../2-lang0c/lang0c.lang0
$(MAKE) -C ../2-lang0c
build/%.it2.c: %.lang0 ../2-lang0c/lang0c.exe build/%.it2.c: %.lang0 ../2-lang0c/lang0c.exe
cat $< | ../2-lang0c/lang0c.exe > $@ cat $< | ../2-lang0c/lang0c.exe > $@.tmp
mv $@.tmp $@
build/%.it2.o: build/%.it2.c build/%.it2.o: build/%.it2.c
$(CC) -o $@ -c $^ -I$(PYPREFIX)/include/python$(PYVERSION) $(CC) -o $@ -c $^ -I$(PYPREFIX)/include/python$(PYVERSION)

View File

@ -2,10 +2,13 @@
/*.it0.c /*.it0.c
/*.it0.o /*.it0.o
/*.it0.py /*.it0.py
/*.it0.py.tmp
/*.it1 /*.it1
/*.it1.c /*.it1.c
/*.it1.o /*.it1.o
/*.it1.py /*.it1.py
/*.it1.py.tmp
/*.it2 /*.it2
/*.it2.c /*.it2.c
/*.it2.c.tmp
/*.it2.o /*.it2.o

View File

@ -0,0 +1,3 @@
main:
check not "1" : "Success"
/

View File

@ -0,0 +1,4 @@
main:
check not "" : "Failure"
emit "Success"
/

View File

@ -0,0 +1,9 @@
func:
return "Success"
/
main:
declare result
calc result func
emit result
/

View File

@ -1,11 +0,0 @@
func result/
main:
declare result
calc result func "Success"
emit result
/
func result:
return result
/

View File

@ -0,0 +1,10 @@
func/
main:
check func : "The function should return 1, even though it is not defined yet"
emit "Success"
/
func:
return "1"
/

View File

@ -0,0 +1,10 @@
func arg/
main:
check func "1" : "The function should return 1, even though it is not defined yet"
emit "Success"
/
func arg:
return arg
/

86
tests/stdinlineno.lang0 Normal file
View File

@ -0,0 +1,86 @@
main:
declare char
declare colno
declare lineno
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char "H" : "Read: H" char
check eq colno "1" : "The column number should not have advanced yet" colno
check eq lineno "1" : "The line number should not have advanced yet" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char "e" : "Read: e" char
check eq colno "2" : "The column number should have been advanced by 1" colno
check eq lineno "1" : "The line number should not have advanced yet" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char "l" : "Read: l" char
check eq colno "3" : "The column number should have been advanced by 2" colno
check eq lineno "1" : "The line number should not have advanced yet" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char "l" : "Read: l" char
check eq colno "4" : "The column number should have been advanced by 3" colno
check eq lineno "1" : "The line number should not have advanced yet" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char "o" : "Read: o" char
check eq colno "5" : "The column number should have been advanced by 4" colno
check eq lineno "1" : "The line number should not have advanced yet" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char "!" : "Read: !" char
check eq colno "6" : "The column number should have been advanced by 5" colno
check eq lineno "1" : "The line number should not have advanced yet" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char eol : "Read: eol" char
check eq colno "7" : "The column number should have been advanced by 6" colno
check eq lineno "1" : "The line number should not have advanced yet" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char eol : "Read: eol" char
check eq colno "1" : "The column number should have been reset" colno
check eq lineno "2" : "The line number should have advanced by 1" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char "T" : "Read: T" char
check eq colno "1" : "The column number should have been reset again" colno
check eq lineno "3" : "The line number should have advanced by 2" lineno
skip
calc char peek
calc colno stdincolno
calc lineno stdinlineno
check eq char "h" : "Read: h" char
check eq colno "2" : "The line number should not have been advanced by 1" colno
check eq lineno "3" : "The line number should not have advanced further" lineno
emit "Success"
/

View File

@ -1,5 +1,6 @@
main: main:
declare result declare result
calc result addstringchar "Succes" "s" calc result addstringchar "abc" "d"
emit result check eq result "abcd" : "Adding abc and d should be abcd"
emit "Success"
/ /

View File

@ -1,8 +1,6 @@
main: main:
declare bool declare bool
calc bool eq "1" "2" calc bool eq "1" "2"
if bool check not bool : "1 should not equal 2"
return
/
emit "Success" emit "Success"
/ /

View File

@ -1,7 +1,5 @@
main: main:
declare bool declare bool
calc bool eq "1" "1" check eq "1" "1" : "1 should equal 1"
if bool emit "Success"
emit "Success"
/
/ /

View File

@ -1,8 +1,6 @@
main: main:
declare bool declare bool
calc bool lt "b" "a" calc bool lt "b" "a"
if bool check not bool : "a should should sort before b"
return
/
emit "Success" emit "Success"
/ /

View File

@ -1,7 +1,5 @@
main: main:
declare bool declare bool
calc bool lt "a" "b" check lt "a" "b" : "a should should sort before b"
if bool emit "Success"
emit "Success"
/
/ /

View File

@ -1,9 +1,6 @@
main: main:
declare char declare char
declare ish
calc char peek calc char peek
calc ish eq char "H" check eq char "H" : "The first byte we're passed is expected to be an 'H'"
if ish emit "Success"
emit "Success"
/
/ /

View File

@ -1,24 +1,12 @@
main: main:
declare char declare char
declare ish
declare iso
declare notish
declare notiso
skip skip
calc char peek calc char peek
calc ish eq char "e" check eq char "e" : "The second byte we're passed is expected to be an 'e'"
calc notish not ish
if notish
return
/
skip skip
skip skip
skip skip
calc char peek calc char peek
calc iso eq char "o" check eq char "o" : "The fifth byte we're passed is expected to be an 'o'"
calc notiso not iso
if notish
return
/
emit "Success" emit "Success"
/ /

3
tests/test-input.txt Normal file
View File

@ -0,0 +1,3 @@
Hello!
This is an example of standard input.