This commit is contained in:
NikolajDanger
2022-06-07 21:59:46 +02:00
parent dece7eb03e
commit de697b121e
5 changed files with 119 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
# About # About
`CENTURION` is the programming language for the modern roman. `CENTVRION` is the programming language for the modern roman.
# Documentation # Documentation
@@ -57,7 +57,7 @@ Variable can consist of lower-case letters, numbers, as well as `_`.
## Data types ## Data types
### NULLUS ### NULLUS
`NULLUS` is a special kind of data type in `CENTURION`, similar to the `null` value in many other languages. `NULLUS` can be 0 if evaluated as an int or float, or an empty string if evaluated as a string. `NULLUS` cannot be evaluated as a boolean. `NULLUS` is a special kind of data type in `CENTVRION`, similar to the `null` value in many other languages. `NULLUS` can be 0 if evaluated as an int or float, or an empty string if evaluated as a string. `NULLUS` cannot be evaluated as a boolean.
### Strings ### Strings
@@ -82,7 +82,7 @@ Integers must be written in roman numerals using the following symbols:
Each of the symbols written by themself is equal to the value of the symbol. Different symbols written from largest to smallest are equal to the sum of the symbols. Two to three of the same symbol written consecutively is equal to the sum of those symbols (only true for `I`s, `X`s, `C`s or `M`s ). A single `I` written before a `V` or `X` is equal to 1 less than the value of the second symbol. Similarly, an `X` written before a `L` or `C` is 10 less than the second symbol, and a `C` written before a `D` or `M` is 100 less than the second symbol. Each of the symbols written by themself is equal to the value of the symbol. Different symbols written from largest to smallest are equal to the sum of the symbols. Two to three of the same symbol written consecutively is equal to the sum of those symbols (only true for `I`s, `X`s, `C`s or `M`s ). A single `I` written before a `V` or `X` is equal to 1 less than the value of the second symbol. Similarly, an `X` written before a `L` or `C` is 10 less than the second symbol, and a `C` written before a `D` or `M` is 100 less than the second symbol.
Because of the restrictions of roman numerals, numbers above 3.999 are impossible to write in the base `CENTURION` syntax. If numbers of that size are required, see the `MAGNUM` module. Because of the restrictions of roman numerals, numbers above 3.999 are impossible to write in the base `CENTVRION` syntax. If numbers of that size are required, see the `MAGNUM` module.
The number 0 can be expressed with the keyword `NULLUS`. The number 0 can be expressed with the keyword `NULLUS`.
@@ -90,7 +90,7 @@ The number 0 can be expressed with the keyword `NULLUS`.
Negative numbers can be expressed as `NULLUS` minus the value. For an explicit definition of negative numbers, see the `SUBNULLA` module. Negative numbers can be expressed as `NULLUS` minus the value. For an explicit definition of negative numbers, see the `SUBNULLA` module.
### Floats ### Floats
The base `CENTURION` syntax does not allow for floats. However, the `FRACTIO` module adds a syntax for fractions. The base `CENTVRION` syntax does not allow for floats. However, the `FRACTIO` module adds a syntax for fractions.
### Booleans ### Booleans
Booleans are denoted with the keywords `VERITAS` for true and `FALSITAS` for false. Booleans are denoted with the keywords `VERITAS` for true and `FALSITAS` for false.
@@ -227,18 +227,18 @@ DICE (INVOCA square XI)
### LONGITUDO ### LONGITUDO
## Modules ## Modules
Modules are additions to the base `CENTURION` syntax. They add or change certain features. Modules are included in your code by having Modules are additions to the base `CENTVRION` syntax. They add or change certain features. Modules are included in your code by having
```VOCA %MODULE NAME%``` ```VOCA %MODULE NAME%```
In the beginning of your source file. In the beginning of your source file.
Unlike many other programming languages with modules, the modules in `CENTURION` are not libraries that can be "imported" from other scripts written in the language. They are features of the compiler, disabled by default. Unlike many other programming languages with modules, the modules in `CENTVRION` are not libraries that can be "imported" from other scripts written in the language. They are features of the compiler, disabled by default.
### FORS ### FORS
```VOCA FORS``` ```VOCA FORS```
The `FORS` module allows you to add randomness to your `CENTURION` program. It adds 2 new built-in functions: `FORTIS_NUMERUS int int` and `FORTIS_ELECTIONIS ['a]`. The `FORS` module allows you to add randomness to your `CENTVRION` program. It adds 2 new built-in functions: `FORTIS_NUMERUS int int` and `FORTIS_ELECTIONIS ['a]`.
`FORTIS_NUMERUS int int` picks a random int in the (inclusive) range of the two given ints. `FORTIS_NUMERUS int int` picks a random int in the (inclusive) range of the two given ints.

View File

@@ -1,5 +1,18 @@
import random
import roman
from rply.token import BaseBox from rply.token import BaseBox
NUMERALS = [
("I", 1),
("V", 5),
("X", 10),
("L", 50),
("C", 100),
("D", 500),
("M", 1000)
]
def rep_join(l): def rep_join(l):
format_string = ',\n'.join( format_string = ',\n'.join(
[repr(i) if not isinstance(i, str) else i for i in l] [repr(i) if not isinstance(i, str) else i for i in l]
@@ -10,6 +23,9 @@ def rep_join(l):
return format_string return format_string
def num_to_int(n):
return roman.fromRoman(n)
class ExpressionStatement(BaseBox): class ExpressionStatement(BaseBox):
def __init__(self, expression) -> None: def __init__(self, expression) -> None:
self.expression = expression self.expression = expression
@@ -18,7 +34,7 @@ class ExpressionStatement(BaseBox):
return self.expression.__repr__() return self.expression.__repr__()
def eval(self, vtable, ftable, modules): def eval(self, vtable, ftable, modules):
self.expression.eval(vtable, ftable, modules) self.expression.eval(vtable.copy(), ftable.copy(), modules)
return vtable, ftable return vtable, ftable
class String(BaseBox): class String(BaseBox):
@@ -28,6 +44,9 @@ class String(BaseBox):
def __repr__(self): def __repr__(self):
return f"String({self.value})" return f"String({self.value})"
def eval(self, *_):
return self.value
class Numeral(BaseBox): class Numeral(BaseBox):
def __init__(self, value) -> None: def __init__(self, value) -> None:
self.value = value self.value = value
@@ -35,6 +54,9 @@ class Numeral(BaseBox):
def __repr__(self): def __repr__(self):
return f"Numeral({self.value})" return f"Numeral({self.value})"
def eval(self, *_):
return num_to_int(self.value)
class Bool(BaseBox): class Bool(BaseBox):
def __init__(self, value) -> None: def __init__(self, value) -> None:
self.value = value self.value = value
@@ -42,6 +64,9 @@ class Bool(BaseBox):
def __repr__(self): def __repr__(self):
return f"Bool({self.value})" return f"Bool({self.value})"
def eval(self, *_):
return self.value
class ModuleCall(BaseBox): class ModuleCall(BaseBox):
def __init__(self, module_name) -> None: def __init__(self, module_name) -> None:
self.module_name = module_name self.module_name = module_name
@@ -56,6 +81,9 @@ class ID(BaseBox):
def __repr__(self) -> str: def __repr__(self) -> str:
return f"ID({self.name})" return f"ID({self.name})"
def eval(self, vtable, *_):
return vtable[self.name]
class Designa(BaseBox): class Designa(BaseBox):
def __init__(self, variable: ID, value) -> None: def __init__(self, variable: ID, value) -> None:
self.id = variable self.id = variable
@@ -67,7 +95,9 @@ class Designa(BaseBox):
return f"Designa(\n {id_string},\n {value_string}\n)" return f"Designa(\n {id_string},\n {value_string}\n)"
def eval(self, vtable, ftable, modules): def eval(self, vtable, ftable, modules):
vtable[self.id.name] = self.value.eval(vtable, ftable, modules) vtable[self.id.name] = self.value.eval(
vtable.copy(), ftable.copy(), modules
)
return vtable, ftable return vtable, ftable
class Defini(BaseBox): class Defini(BaseBox):
@@ -92,6 +122,14 @@ class Redi(BaseBox):
values_string = f"[{rep_join(self.values)}]" values_string = f"[{rep_join(self.values)}]"
return f"Redi({values_string})" return f"Redi({values_string})"
class Erumpe(BaseBox):
def __repr__(self) -> str:
return "Erumpe()"
def eval(self, vtable, ftable, _):
vtable["ERUMPE"] = True
return vtable, ftable
class Nullus(BaseBox): class Nullus(BaseBox):
def __repr__(self) -> str: def __repr__(self) -> str:
return "Nullus()" return "Nullus()"
@@ -109,6 +147,19 @@ class BinOp(BaseBox):
binop_string = rep_join([self.left, self.right, self.op]) binop_string = rep_join([self.left, self.right, self.op])
return f"BinOp({binop_string})" return f"BinOp({binop_string})"
def eval(self, vtable, ftable, modules):
left = self.left.eval(vtable.copy(), ftable.copy(), modules)
right = self.right.eval(vtable.copy(), ftable.copy(), modules)
match self.op:
case "SYMBOL_PLUS":
return left + right
case "KEYWORD_MINUS":
return left < right
case "KEYWORD_PLUS":
return left > right
case _:
raise Exception(self.op)
class SiStatement(BaseBox): class SiStatement(BaseBox):
def __init__(self, test, statements, else_part) -> None: def __init__(self, test, statements, else_part) -> None:
self.test = test self.test = test
@@ -122,6 +173,20 @@ class SiStatement(BaseBox):
si_string = rep_join([test, statements, else_part]) si_string = rep_join([test, statements, else_part])
return f"Si({si_string})" return f"Si({si_string})"
def eval(self, vtable, ftable, modules):
if self.test.eval(vtable, ftable, modules):
for statement in self.statements:
vtable, ftable = statement.eval(
vtable, ftable, modules
)
else:
for statement in self.else_part:
vtable, ftable = statement.eval(
vtable, ftable, modules
)
return vtable, ftable
class DumStatement(BaseBox): class DumStatement(BaseBox):
def __init__(self, test, statements) -> None: def __init__(self, test, statements) -> None:
self.test = test self.test = test
@@ -135,7 +200,16 @@ class DumStatement(BaseBox):
def eval(self, vtable, ftable, modules): def eval(self, vtable, ftable, modules):
while not self.test.eval(vtable, ftable, modules): while not self.test.eval(vtable, ftable, modules):
pass for statement in self.statements:
vtable, ftable = statement.eval(
vtable, ftable, modules
)
if vtable["ERUMPE"]:
break
if vtable["ERUMPE"]:
vtable["ERUMPE"] = False
break
return vtable, ftable return vtable, ftable
@@ -159,8 +233,25 @@ class BuiltIn(BaseBox):
builtin_string = rep_join([self.builtin, parameter_string]) builtin_string = rep_join([self.builtin, parameter_string])
return f"Builtin({builtin_string})" return f"Builtin({builtin_string})"
def eval(self, vtable, ftable, _): def eval(self, vtable, ftable, modules):
parameters = [
i.eval(vtable.copy(), ftable.copy(), modules)
for i in self.parameters
]
match self.builtin:
case "AUDI_NUMERUS":
return num_to_int(input())
case "DICE":
print(' '.join(parameters))
return None return None
case "ERUMPE":
vtable["ERUMPE"] = True
return None
case "FORTIS_NUMERUS":
return random.randint(parameters[0], parameters[1])
case _:
raise Exception(self.builtin)
class Program(BaseBox): class Program(BaseBox):
def __init__(self, module_calls: list[ModuleCall], statements) -> None: def __init__(self, module_calls: list[ModuleCall], statements) -> None:
@@ -173,7 +264,7 @@ class Program(BaseBox):
return f"{modules_string},\n{statements_string}" return f"{modules_string},\n{statements_string}"
def eval(self): def eval(self):
vtable = {} vtable = {"ERUMPE": False}
ftable = {} ftable = {}
modules = [module.module_name for module in self.modules] modules = [module.module_name for module in self.modules]

View File

@@ -1,11 +1,14 @@
from rply import LexerGenerator from rply import LexerGenerator
valid_characters = '|'.join(list("abcdefghiklmnopqrstvxyz_"))
keyword_tokens = [("KEYWORD_"+i, i) for i in [ keyword_tokens = [("KEYWORD_"+i, i) for i in [
"ALUID", "ALUID",
"DEFINI", "DEFINI",
"DESIGNA", "DESIGNA",
"DONICUM", "DONICUM",
"DUM", "DUM",
"ERUMPE",
"EST", "EST",
"FACE", "FACE",
"FALSITAS", "FALSITAS",
@@ -27,7 +30,6 @@ builtin_tokens = [("BUILTIN", i) for i in [
"AUDI_NUMERUS", "AUDI_NUMERUS",
"AUDI", "AUDI",
"DICE", "DICE",
"ERUMPE",
"FORTIS_NUMERUS", "FORTIS_NUMERUS",
"FORTIS_ELECTIONIS", "FORTIS_ELECTIONIS",
"LONGITUDO" "LONGITUDO"
@@ -69,7 +71,7 @@ all_tokens = (
symbol_tokens + symbol_tokens +
data_tokens + data_tokens +
whitespace_tokens + whitespace_tokens +
[("ID", r"([a-z]|_)+")] [("ID", f"({valid_characters})+")]
) )
class Lexer(): class Lexer():

11
main.py
View File

@@ -5,13 +5,13 @@ text_input = """
VOCA FORS VOCA FORS
DESIGNA correct UT FORTIS_NUMERUS I C DESIGNA correct UT FORTIS_NUMERUS I C
DESIGNA guess UT NULLUS DESIGNA gvess UT NULLUS
DUM FALSITAS FACE { DUM FALSITAS FACE {
DESIGNA guess UT AUDI_NUMERUS DESIGNA gvess UT AUDI_NUMERUS
SI guess MINUS correct TUNC { SI gvess MINUS correct TUNC {
DICE "Too low!" DICE "Too low!"
} ALUID SI guess PLUS correct TUNC { } ALUID SI gvess PLUS correct TUNC {
DICE "Too high!" DICE "Too high!"
} ALUID { } ALUID {
ERUMPE ERUMPE
@@ -28,4 +28,5 @@ parser = pg.get_parser()
tokens = lexer.lex(text_input) tokens = lexer.lex(text_input)
print(parser.parse(tokens)) x = parser.parse(tokens)
x.eval()

View File

@@ -77,7 +77,7 @@ class Parser():
@self.pg.production('expression : DATA_STRING') @self.pg.production('expression : DATA_STRING')
def expression_string(tokens): def expression_string(tokens):
return ast_nodes.String(tokens[0].value) return ast_nodes.String(tokens[0].value[1:-1])
@self.pg.production('expression : DATA_NUMERAL') @self.pg.production('expression : DATA_NUMERAL')
def expression_numeral(tokens): def expression_numeral(tokens):
@@ -120,6 +120,10 @@ class Parser():
def dum_statement(tokens): def dum_statement(tokens):
return tokens[0] return tokens[0]
@self.pg.production('statement : KEYWORD_ERUMPE')
def erumpe(_):
return ast_nodes.Erumpe()
@self.pg.production('si_statement : KEYWORD_SI expression KEYWORD_TUNC SYMBOL_LCURL opt_newline statements opt_newline SYMBOL_RCURL opt_newline aluid_statement') @self.pg.production('si_statement : KEYWORD_SI expression KEYWORD_TUNC SYMBOL_LCURL opt_newline statements opt_newline SYMBOL_RCURL opt_newline aluid_statement')
def si(tokens): def si(tokens):
return ast_nodes.SiStatement(tokens[1], tokens[5], tokens[9]) return ast_nodes.SiStatement(tokens[1], tokens[5], tokens[9])